Skip to content

Commit eb22772

Browse files
committed
feat: coloring
1 parent c7de85d commit eb22772

20 files changed

Lines changed: 3654 additions & 73 deletions

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/nanorele/gio/app"
1111
)
1212

13-
const appTitle = "T (0.4.1)"
13+
const appTitle = "T [0.4.1]"
1414
const bugReportURL = "https://github.com/prostraction/tracto/issues/new"
1515

1616
func main() {

internal/ui/app.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,9 +1028,87 @@ func (ui *AppUI) layoutApp(gtx layout.Context) layout.Dimensions {
10281028
ui.layoutVarPopup(gtx)
10291029
}
10301030

1031+
// Settings color picker — rendered as a deferred overlay on top of
1032+
// everything so it floats over the rest of the settings panel
1033+
// (matches the method dropdown's behaviour). The picker's own
1034+
// gestures handle drag input within the deferred area.
1035+
if ui.SettingsOpen && ui.SettingsState != nil && ui.SettingsState.ColorPicker.isOpen() {
1036+
ui.layoutColorPickerOverlay(gtx)
1037+
}
1038+
10311039
return dim
10321040
}
10331041

1042+
// layoutColorPickerOverlay renders the inline HSV picker as a deferred
1043+
// overlay anchored near where the user clicked the swatch. Uses
1044+
// op.Defer so the picker draws on top of all preceding ops in the
1045+
// current frame. A full-window backdrop registered inside the same
1046+
// macro detects Press events outside the picker rect and dismisses it
1047+
// — same click-outside-to-close pattern the var popup uses.
1048+
func (ui *AppUI) layoutColorPickerOverlay(gtx layout.Context) {
1049+
p := &ui.SettingsState.ColorPicker
1050+
pickerW := gtx.Dp(unit.Dp(240))
1051+
pickerH := gtx.Dp(unit.Dp(216))
1052+
gap := gtx.Dp(unit.Dp(6))
1053+
1054+
// Anchor below the cursor, flipped above if there's no room. Then
1055+
// clamp to keep the popup fully on-screen.
1056+
px := int(p.anchor.X) + gap
1057+
py := int(p.anchor.Y) + gap
1058+
if px+pickerW > gtx.Constraints.Max.X {
1059+
px = gtx.Constraints.Max.X - pickerW - gap
1060+
}
1061+
if py+pickerH > gtx.Constraints.Max.Y {
1062+
py = int(p.anchor.Y) - pickerH - gap
1063+
}
1064+
if px < 0 {
1065+
px = 0
1066+
}
1067+
if py < 0 {
1068+
py = 0
1069+
}
1070+
pickerRect := image.Rect(px, py, px+pickerW, py+pickerH)
1071+
1072+
macro := op.Record(gtx.Ops)
1073+
1074+
// Backdrop covers the whole window. event.Op registers a tag whose
1075+
// pointer.Filter catches every Press; presses inside pickerRect are
1076+
// the user driving the picker (drag SV, hue, click Close), so we
1077+
// ignore them — anything else means "click outside" and closes the
1078+
// picker. Backdrop is drawn before the picker so the picker still
1079+
// renders on top within the same op.Defer.
1080+
backdropStack := clip.Rect{Max: gtx.Constraints.Max}.Push(gtx.Ops)
1081+
for {
1082+
ev, ok := gtx.Event(pointer.Filter{
1083+
Target: &p.backdrop,
1084+
Kinds: pointer.Press,
1085+
})
1086+
if !ok {
1087+
break
1088+
}
1089+
pe, ok := ev.(pointer.Event)
1090+
if !ok {
1091+
continue
1092+
}
1093+
pos := image.Pt(int(pe.Position.X), int(pe.Position.Y))
1094+
if pos.In(pickerRect) {
1095+
continue
1096+
}
1097+
p.closePicker()
1098+
}
1099+
event.Op(gtx.Ops, &p.backdrop)
1100+
backdropStack.Pop()
1101+
1102+
// Picker on top of the backdrop.
1103+
pickerOff := op.Offset(image.Pt(px, py)).Push(gtx.Ops)
1104+
pickerGtx := gtx
1105+
pickerGtx.Constraints.Min = image.Pt(pickerW, pickerH)
1106+
pickerGtx.Constraints.Max = pickerGtx.Constraints.Min
1107+
renderColorPicker(pickerGtx, ui.Theme, p)
1108+
pickerOff.Pop()
1109+
op.Defer(gtx.Ops, macro.Stop())
1110+
}
1111+
10341112
func (ui *AppUI) layoutVarPopup(gtx layout.Context) {
10351113
popupW := gtx.Dp(unit.Dp(360))
10361114
popupH := gtx.Dp(unit.Dp(180))

0 commit comments

Comments
 (0)