summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.go319
1 files changed, 215 insertions, 104 deletions
diff --git a/src/main.go b/src/main.go
index 910aef8..f2e85f3 100644
--- a/src/main.go
+++ b/src/main.go
@@ -22,6 +22,8 @@ const (
22 appTitle = "kjagave" 22 appTitle = "kjagave"
23 appVersion = "20260315-0200" 23 appVersion = "20260315-0200"
24 maxHistoryLen = 250 24 maxHistoryLen = 250
25 cardImageW = 150
26 cardImageH = 98
25) 27)
26 28
27type SavedColor struct { 29type SavedColor struct {
@@ -45,6 +47,7 @@ type SwatchCard struct {
45 47
46type App struct { 48type App struct {
47 window *gtk.Window 49 window *gtk.Window
50 css *gtk.CssProvider
48 51
49 colorButton *gtk.ColorButton 52 colorButton *gtk.ColorButton
50 hexEntry *gtk.Entry 53 hexEntry *gtk.Entry
@@ -60,11 +63,12 @@ type App struct {
60 63
61 swatchCards []SwatchCard 64 swatchCards []SwatchCard
62 65
63 paletteStore *gtk.ListStore 66 paletteGrid *gtk.Grid
64 paletteView *gtk.TreeView 67 paletteScroll *gtk.ScrolledWindow
65 68
66 favoritesStore *gtk.ListStore 69 favoritesStore *gtk.ListStore
67 favoritesView *gtk.TreeView 70 favoritesView *gtk.TreeView
71 renameFavBtn *gtk.Button
68 removeFavBtn *gtk.Button 72 removeFavBtn *gtk.Button
69 clearFavBtn *gtk.Button 73 clearFavBtn *gtk.Button
70 selectedIter *gtk.TreeIter 74 selectedIter *gtk.TreeIter
@@ -114,7 +118,7 @@ func main() {
114 app.createUI() 118 app.createUI()
115 app.refreshFavoritesView() 119 app.refreshFavoritesView()
116 app.restoreStartupState() 120 app.restoreStartupState()
117 app.populatePaletteList() 121 app.populatePaletteGrid()
118 app.updateSchemePreview() 122 app.updateSchemePreview()
119 app.updateActionStates() 123 app.updateActionStates()
120 124
@@ -128,58 +132,71 @@ func (app *App) createUI() {
128 log.Fatal("Unable to create window:", err) 132 log.Fatal("Unable to create window:", err)
129 } 133 }
130 app.window.SetTitle(appTitle) 134 app.window.SetTitle(appTitle)
131 app.window.SetDefaultSize(980, 680) 135 app.window.SetDefaultSize(640, 480)
132 app.window.Connect("destroy", func() { 136 app.window.Connect("destroy", func() {
133 app.saveConfig() 137 app.saveConfig()
134 gtk.MainQuit() 138 gtk.MainQuit()
135 }) 139 })
136 140
137 root, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 8) 141 root, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 2)
138 root.SetMarginTop(8) 142 root.SetMarginTop(2)
139 root.SetMarginBottom(8) 143 root.SetMarginBottom(2)
140 root.SetMarginStart(8) 144 root.SetMarginStart(2)
141 root.SetMarginEnd(8) 145 root.SetMarginEnd(2)
142 146
143 toolbar, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 4) 147 menuBar := app.buildMenuBar()
148 root.PackStart(menuBar, false, false, 0)
149 app.initCompactButtonCSS()
150
151 toolbar, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 3)
144 app.historyBackBtn, _ = gtk.ButtonNewWithLabel("Back") 152 app.historyBackBtn, _ = gtk.ButtonNewWithLabel("Back")
153 app.setButtonIcon(app.historyBackBtn, "go-previous")
145 app.historyBackBtn.Connect("clicked", func() { app.navigateHistory(-1) }) 154 app.historyBackBtn.Connect("clicked", func() { app.navigateHistory(-1) })
146 toolbar.PackStart(app.historyBackBtn, false, false, 0) 155 toolbar.PackStart(app.historyBackBtn, false, false, 0)
147 156
148 app.historyFwdBtn, _ = gtk.ButtonNewWithLabel("Forward") 157 app.historyFwdBtn, _ = gtk.ButtonNewWithLabel("Forward")
158 app.setButtonIcon(app.historyFwdBtn, "go-next")
149 app.historyFwdBtn.Connect("clicked", func() { app.navigateHistory(1) }) 159 app.historyFwdBtn.Connect("clicked", func() { app.navigateHistory(1) })
150 toolbar.PackStart(app.historyFwdBtn, false, false, 0) 160 toolbar.PackStart(app.historyFwdBtn, false, false, 0)
151 161
152 randomBtn, _ := gtk.ButtonNewWithLabel("Random") 162 randomBtn, _ := gtk.ButtonNewWithLabel("Random")
163 app.setButtonIcon(randomBtn, "view-refresh")
153 randomBtn.Connect("clicked", func() { app.randomizeColor() }) 164 randomBtn.Connect("clicked", func() { app.randomizeColor() })
154 toolbar.PackStart(randomBtn, false, false, 0) 165 toolbar.PackStart(randomBtn, false, false, 0)
155 166
156 app.lightenBtn, _ = gtk.ButtonNewWithLabel("Lighten") 167 app.lightenBtn, _ = gtk.ButtonNewWithLabel("Lighten")
168 app.setButtonIcon(app.lightenBtn, "go-up")
157 app.lightenBtn.Connect("clicked", func() { app.adjustSV(0, 5) }) 169 app.lightenBtn.Connect("clicked", func() { app.adjustSV(0, 5) })
158 toolbar.PackStart(app.lightenBtn, false, false, 0) 170 toolbar.PackStart(app.lightenBtn, false, false, 0)
159 171
160 app.darkenBtn, _ = gtk.ButtonNewWithLabel("Darken") 172 app.darkenBtn, _ = gtk.ButtonNewWithLabel("Darken")
173 app.setButtonIcon(app.darkenBtn, "go-down")
161 app.darkenBtn.Connect("clicked", func() { app.adjustSV(0, -5) }) 174 app.darkenBtn.Connect("clicked", func() { app.adjustSV(0, -5) })
162 toolbar.PackStart(app.darkenBtn, false, false, 0) 175 toolbar.PackStart(app.darkenBtn, false, false, 0)
163 176
164 app.saturateBtn, _ = gtk.ButtonNewWithLabel("Saturate") 177 app.saturateBtn, _ = gtk.ButtonNewWithLabel("Saturate")
178 app.setButtonIcon(app.saturateBtn, "list-add")
165 app.saturateBtn.Connect("clicked", func() { app.adjustSV(5, 0) }) 179 app.saturateBtn.Connect("clicked", func() { app.adjustSV(5, 0) })
166 toolbar.PackStart(app.saturateBtn, false, false, 0) 180 toolbar.PackStart(app.saturateBtn, false, false, 0)
167 181
168 app.desaturateBtn, _ = gtk.ButtonNewWithLabel("Desaturate") 182 app.desaturateBtn, _ = gtk.ButtonNewWithLabel("Desaturate")
183 app.setButtonIcon(app.desaturateBtn, "list-remove")
169 app.desaturateBtn.Connect("clicked", func() { app.adjustSV(-5, 0) }) 184 app.desaturateBtn.Connect("clicked", func() { app.adjustSV(-5, 0) })
170 toolbar.PackStart(app.desaturateBtn, false, false, 0) 185 toolbar.PackStart(app.desaturateBtn, false, false, 0)
171 186
172 pasteBtn, _ := gtk.ButtonNewWithLabel("Paste") 187 pasteBtn, _ := gtk.ButtonNewWithLabel("Paste")
188 app.setButtonIcon(pasteBtn, "edit-paste")
173 pasteBtn.Connect("clicked", func() { app.pasteColorFromClipboard() }) 189 pasteBtn.Connect("clicked", func() { app.pasteColorFromClipboard() })
174 toolbar.PackStart(pasteBtn, false, false, 0) 190 toolbar.PackStart(pasteBtn, false, false, 0)
175 191
176 aboutBtn, _ := gtk.ButtonNewWithLabel("About") 192 aboutBtn, _ := gtk.ButtonNewWithLabel("About")
193 app.setButtonIcon(aboutBtn, "help-about")
177 aboutBtn.Connect("clicked", func() { app.onAboutClicked() }) 194 aboutBtn.Connect("clicked", func() { app.onAboutClicked() })
178 toolbar.PackStart(aboutBtn, false, false, 0) 195 toolbar.PackStart(aboutBtn, false, false, 0)
179 196
180 root.PackStart(toolbar, false, false, 0) 197 root.PackStart(toolbar, false, false, 0)
181 198
182 schemeRow, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 8) 199 schemeRow, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 4)
183 app.swatchCards = make([]SwatchCard, 0, 4) 200 app.swatchCards = make([]SwatchCard, 0, 4)
184 for i := 0; i < 4; i++ { 201 for i := 0; i < 4; i++ {
185 card := app.newSwatchCard() 202 card := app.newSwatchCard()
@@ -200,7 +217,7 @@ func (app *App) createUI() {
200 } 217 }
201 root.PackStart(schemeRow, false, false, 0) 218 root.PackStart(schemeRow, false, false, 0)
202 219
203 controlRow, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 8) 220 controlRow, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6)
204 app.currentColor = gdk.NewRGBA() 221 app.currentColor = gdk.NewRGBA()
205 app.currentColor.Parse("#0066FF") 222 app.currentColor.Parse("#0066FF")
206 223
@@ -234,11 +251,8 @@ func (app *App) createUI() {
234 app.hexEntry.Connect("activate", func() { app.applyHexEntry() }) 251 app.hexEntry.Connect("activate", func() { app.applyHexEntry() })
235 controlRow.PackStart(app.hexEntry, false, false, 0) 252 controlRow.PackStart(app.hexEntry, false, false, 0)
236 253
237 useHexBtn, _ := gtk.ButtonNewWithLabel("Use")
238 useHexBtn.Connect("clicked", func() { app.applyHexEntry() })
239 controlRow.PackStart(useHexBtn, false, false, 0)
240
241 pickBtn, _ := gtk.ButtonNewWithLabel("Pick from Screen") 254 pickBtn, _ := gtk.ButtonNewWithLabel("Pick from Screen")
255 app.setButtonIcon(pickBtn, "color-select")
242 pickBtn.Connect("clicked", func() { 256 pickBtn.Connect("clicked", func() {
243 clr, err := app.pickColorFromScreen() 257 clr, err := app.pickColorFromScreen()
244 if err == nil { 258 if err == nil {
@@ -248,6 +262,7 @@ func (app *App) createUI() {
248 controlRow.PackStart(pickBtn, false, false, 0) 262 controlRow.PackStart(pickBtn, false, false, 0)
249 263
250 copyBtn, _ := gtk.ButtonNewWithLabel("Copy") 264 copyBtn, _ := gtk.ButtonNewWithLabel("Copy")
265 app.setButtonIcon(copyBtn, "edit-copy")
251 copyBtn.Connect("clicked", func() { 266 copyBtn.Connect("clicked", func() {
252 clipboard, _ := gtk.ClipboardGet(gdk.SELECTION_CLIPBOARD) 267 clipboard, _ := gtk.ClipboardGet(gdk.SELECTION_CLIPBOARD)
253 clipboard.SetText(app.currentHex) 268 clipboard.SetText(app.currentHex)
@@ -256,14 +271,23 @@ func (app *App) createUI() {
256 271
257 root.PackStart(controlRow, false, false, 0) 272 root.PackStart(controlRow, false, false, 0)
258 273
259 lower, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 8) 274 lower, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6)
260 275
261 paletteFrame, _ := gtk.FrameNew("Palette") 276 paletteFrame, _ := gtk.FrameNew("Palette")
262 paletteBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 6) 277 paletteBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 6)
263 paletteBox.SetMarginTop(8) 278 paletteBox.SetMarginTop(5)
264 paletteBox.SetMarginBottom(8) 279 paletteBox.SetMarginBottom(5)
265 paletteBox.SetMarginStart(8) 280 paletteBox.SetMarginStart(5)
266 paletteBox.SetMarginEnd(8) 281 paletteBox.SetMarginEnd(5)
282
283 app.paletteScroll, _ = gtk.ScrolledWindowNew(nil, nil)
284 app.paletteScroll.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
285 app.paletteScroll.SetSizeRequest(430, 150)
286 app.paletteGrid, _ = gtk.GridNew()
287 app.paletteGrid.SetRowSpacing(0)
288 app.paletteGrid.SetColumnSpacing(0)
289 app.paletteScroll.Add(app.paletteGrid)
290 paletteBox.PackStart(app.paletteScroll, true, true, 0)
267 291
268 app.paletteCombo, _ = gtk.ComboBoxTextNew() 292 app.paletteCombo, _ = gtk.ComboBoxTextNew()
269 for _, name := range paletteNames { 293 for _, name := range paletteNames {
@@ -271,45 +295,23 @@ func (app *App) createUI() {
271 } 295 }
272 app.paletteCombo.SetActive(0) 296 app.paletteCombo.SetActive(0)
273 app.paletteCombo.Connect("changed", func() { 297 app.paletteCombo.Connect("changed", func() {
274 app.populatePaletteList() 298 app.populatePaletteGrid()
275 app.saveConfig() 299 app.saveConfig()
276 }) 300 })
277 paletteBox.PackStart(app.paletteCombo, false, false, 0) 301 paletteBox.PackStart(app.paletteCombo, false, false, 0)
278
279 paletteScroll, _ := gtk.ScrolledWindowNew(nil, nil)
280 paletteScroll.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
281 paletteScroll.SetSizeRequest(430, 260)
282
283 app.paletteStore, _ = gtk.ListStoreNew(gdk.PixbufGetType(), glib.TYPE_STRING)
284 app.paletteView, _ = gtk.TreeViewNew()
285 app.paletteView.SetModel(app.paletteStore)
286 app.paletteView.SetHeadersVisible(false)
287 paletteCol, _ := gtk.TreeViewColumnNew()
288 palettePix, _ := gtk.CellRendererPixbufNew()
289 paletteCol.PackStart(palettePix, false)
290 paletteCol.AddAttribute(palettePix, "pixbuf", 0)
291 paletteText, _ := gtk.CellRendererTextNew()
292 paletteCol.PackStart(paletteText, true)
293 paletteCol.AddAttribute(paletteText, "text", 1)
294 app.paletteView.AppendColumn(paletteCol)
295 palSel, _ := app.paletteView.GetSelection()
296 palSel.SetMode(gtk.SELECTION_SINGLE)
297 palSel.Connect("changed", app.onPaletteSelectionChanged)
298 paletteScroll.Add(app.paletteView)
299 paletteBox.PackStart(paletteScroll, true, true, 0)
300 paletteFrame.Add(paletteBox) 302 paletteFrame.Add(paletteBox)
301 lower.PackStart(paletteFrame, true, true, 0) 303 lower.PackStart(paletteFrame, true, true, 0)
302 304
303 favFrame, _ := gtk.FrameNew("Favorites") 305 favFrame, _ := gtk.FrameNew("Favorites")
304 favBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 6) 306 favBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 6)
305 favBox.SetMarginTop(8) 307 favBox.SetMarginTop(5)
306 favBox.SetMarginBottom(8) 308 favBox.SetMarginBottom(5)
307 favBox.SetMarginStart(8) 309 favBox.SetMarginStart(5)
308 favBox.SetMarginEnd(8) 310 favBox.SetMarginEnd(5)
309 311
310 favScroll, _ := gtk.ScrolledWindowNew(nil, nil) 312 favScroll, _ := gtk.ScrolledWindowNew(nil, nil)
311 favScroll.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 313 favScroll.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
312 favScroll.SetSizeRequest(260, 260) 314 favScroll.SetSizeRequest(230, 180)
313 315
314 app.favoritesStore, _ = gtk.ListStoreNew(gdk.PixbufGetType(), glib.TYPE_STRING, glib.TYPE_STRING) 316 app.favoritesStore, _ = gtk.ListStoreNew(gdk.PixbufGetType(), glib.TYPE_STRING, glib.TYPE_STRING)
315 app.favoritesView, _ = gtk.TreeViewNew() 317 app.favoritesView, _ = gtk.TreeViewNew()
@@ -332,20 +334,31 @@ func (app *App) createUI() {
332 favSel, _ := app.favoritesView.GetSelection() 334 favSel, _ := app.favoritesView.GetSelection()
333 favSel.SetMode(gtk.SELECTION_SINGLE) 335 favSel.SetMode(gtk.SELECTION_SINGLE)
334 favSel.Connect("changed", app.onFavoriteSelectionChanged) 336 favSel.Connect("changed", app.onFavoriteSelectionChanged)
337 app.favoritesView.Connect("row-activated", func() {
338 app.renameSelectedFavorite()
339 })
335 340
336 favScroll.Add(app.favoritesView) 341 favScroll.Add(app.favoritesView)
337 favBox.PackStart(favScroll, true, true, 0) 342 favBox.PackStart(favScroll, true, true, 0)
338 343
339 favBtns, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 4) 344 favBtns, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 4)
340 addFavBtn, _ := gtk.ButtonNewWithLabel("+") 345 addFavBtn, _ := gtk.ButtonNewWithLabel("+")
346 app.setButtonIcon(addFavBtn, "list-add")
341 addFavBtn.Connect("clicked", func() { app.addCurrentToFavorites() }) 347 addFavBtn.Connect("clicked", func() { app.addCurrentToFavorites() })
342 favBtns.PackStart(addFavBtn, true, true, 0) 348 favBtns.PackStart(addFavBtn, true, true, 0)
343 349
344 app.removeFavBtn, _ = gtk.ButtonNewWithLabel("-") 350 app.removeFavBtn, _ = gtk.ButtonNewWithLabel("-")
351 app.setButtonIcon(app.removeFavBtn, "list-remove")
345 app.removeFavBtn.Connect("clicked", func() { app.removeSelectedFavorite() }) 352 app.removeFavBtn.Connect("clicked", func() { app.removeSelectedFavorite() })
346 favBtns.PackStart(app.removeFavBtn, true, true, 0) 353 favBtns.PackStart(app.removeFavBtn, true, true, 0)
347 354
355 app.renameFavBtn, _ = gtk.ButtonNewWithLabel("Rename")
356 app.setButtonIcon(app.renameFavBtn, "document-edit")
357 app.renameFavBtn.Connect("clicked", func() { app.renameSelectedFavorite() })
358 favBtns.PackStart(app.renameFavBtn, true, true, 0)
359
348 app.clearFavBtn, _ = gtk.ButtonNewWithLabel("Clear") 360 app.clearFavBtn, _ = gtk.ButtonNewWithLabel("Clear")
361 app.setButtonIcon(app.clearFavBtn, "edit-clear")
349 app.clearFavBtn.Connect("clicked", func() { 362 app.clearFavBtn.Connect("clicked", func() {
350 app.savedColors = nil 363 app.savedColors = nil
351 app.refreshFavoritesView() 364 app.refreshFavoritesView()
@@ -353,10 +366,6 @@ func (app *App) createUI() {
353 }) 366 })
354 favBtns.PackStart(app.clearFavBtn, true, true, 0) 367 favBtns.PackStart(app.clearFavBtn, true, true, 0)
355 368
356 exportBtn, _ := gtk.ButtonNewWithLabel("Export GPL")
357 exportBtn.Connect("clicked", func() { app.exportFavoritesGPLToDefaultPath() })
358 favBtns.PackStart(exportBtn, true, true, 0)
359
360 favBox.PackStart(favBtns, false, false, 0) 369 favBox.PackStart(favBtns, false, false, 0)
361 favFrame.Add(favBox) 370 favFrame.Add(favBox)
362 lower.PackStart(favFrame, false, false, 0) 371 lower.PackStart(favFrame, false, false, 0)
@@ -371,18 +380,83 @@ func (app *App) createUI() {
371 app.window.ShowAll() 380 app.window.ShowAll()
372} 381}
373 382
383func (app *App) buildMenuBar() *gtk.MenuBar {
384 menuBar, _ := gtk.MenuBarNew()
385
386 fileTop, _ := gtk.MenuItemNewWithLabel("File")
387 fileMenu, _ := gtk.MenuNew()
388 random, _ := gtk.MenuItemNewWithLabel("Random")
389 random.Connect("activate", func() { app.randomizeColor() })
390 fileMenu.Append(random)
391 quit, _ := gtk.MenuItemNewWithLabel("Quit")
392 quit.Connect("activate", func() {
393 app.saveConfig()
394 gtk.MainQuit()
395 })
396 fileMenu.Append(quit)
397 fileTop.SetSubmenu(fileMenu)
398 menuBar.Append(fileTop)
399
400 editTop, _ := gtk.MenuItemNewWithLabel("Edit")
401 editMenu, _ := gtk.MenuNew()
402 paste, _ := gtk.MenuItemNewWithLabel("Paste")
403 paste.Connect("activate", func() { app.pasteColorFromClipboard() })
404 editMenu.Append(paste)
405 editTop.SetSubmenu(editMenu)
406 menuBar.Append(editTop)
407
408 favTop, _ := gtk.MenuItemNewWithLabel("Favorites")
409 favMenu, _ := gtk.MenuNew()
410 add, _ := gtk.MenuItemNewWithLabel("Add Current")
411 add.Connect("activate", func() { app.addCurrentToFavorites() })
412 favMenu.Append(add)
413 rename, _ := gtk.MenuItemNewWithLabel("Rename Selected")
414 rename.Connect("activate", func() { app.renameSelectedFavorite() })
415 favMenu.Append(rename)
416 favTop.SetSubmenu(favMenu)
417 menuBar.Append(favTop)
418
419 helpTop, _ := gtk.MenuItemNewWithLabel("Help")
420 helpMenu, _ := gtk.MenuNew()
421 about, _ := gtk.MenuItemNewWithLabel("About")
422 about.Connect("activate", func() { app.onAboutClicked() })
423 helpMenu.Append(about)
424 helpTop.SetSubmenu(helpMenu)
425 menuBar.Append(helpTop)
426
427 return menuBar
428}
429
430func (app *App) initCompactButtonCSS() {
431 app.css, _ = gtk.CssProviderNew()
432 css := "button { padding: 1px 4px; min-height: 0; min-width: 0; } .palette-swatch { padding: 0; border-width: 0; border-radius: 0; }"
433 _ = app.css.LoadFromData(css)
434 if screen, err := gdk.ScreenGetDefault(); err == nil && screen != nil {
435 gtk.AddProviderForScreen(screen, app.css, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
436 }
437}
438
439func (app *App) setButtonIcon(btn *gtk.Button, iconName string) {
440 img, err := gtk.ImageNewFromIconName(iconName, gtk.ICON_SIZE_BUTTON)
441 if err != nil || img == nil {
442 return
443 }
444 btn.SetImage(img)
445 btn.SetAlwaysShowImage(true)
446}
447
374func (app *App) newSwatchCard() SwatchCard { 448func (app *App) newSwatchCard() SwatchCard {
375 button, _ := gtk.ButtonNew() 449 button, _ := gtk.ButtonNew()
376 button.SetSizeRequest(220, 240) 450 button.SetSizeRequest(170, 185)
377 451
378 vbox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 4) 452 vbox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 4)
379 vbox.SetMarginTop(8) 453 vbox.SetMarginTop(4)
380 vbox.SetMarginBottom(8) 454 vbox.SetMarginBottom(4)
381 vbox.SetMarginStart(8) 455 vbox.SetMarginStart(4)
382 vbox.SetMarginEnd(8) 456 vbox.SetMarginEnd(4)
383 457
384 image, _ := gtk.ImageNew() 458 image, _ := gtk.ImageNew()
385 image.SetFromPixbuf(solidPixbuf("#000000", 220, 150)) 459 image.SetFromPixbuf(solidPixbuf("#000000", cardImageW, cardImageH))
386 vbox.PackStart(image, false, false, 0) 460 vbox.PackStart(image, false, false, 0)
387 461
388 label, _ := gtk.LabelNew("") 462 label, _ := gtk.LabelNew("")
@@ -421,17 +495,19 @@ func (app *App) updateSchemePreview() {
421 colors := generateScheme(app.currentColor, app.activeSchemeName()) 495 colors := generateScheme(app.currentColor, app.activeSchemeName())
422 for i := 0; i < len(app.swatchCards); i++ { 496 for i := 0; i < len(app.swatchCards); i++ {
423 if i >= len(colors) { 497 if i >= len(colors) {
498 app.swatchCards[i].hex = ""
424 app.swatchCards[i].button.Hide() 499 app.swatchCards[i].button.Hide()
425 continue 500 continue
426 } 501 }
427 app.swatchCards[i].button.Show() 502 app.swatchCards[i].button.Show()
503 app.swatchCards[i].button.SetSensitive(true)
428 hex := rgbaToHex(colors[i]) 504 hex := rgbaToHex(colors[i])
429 h, s, v := rgbToHSV(colors[i]) 505 h, s, v := rgbToHSV(colors[i])
430 r := int(math.Round(colors[i].GetRed() * 255)) 506 r := int(math.Round(colors[i].GetRed() * 255))
431 g := int(math.Round(colors[i].GetGreen() * 255)) 507 g := int(math.Round(colors[i].GetGreen() * 255))
432 b := int(math.Round(colors[i].GetBlue() * 255)) 508 b := int(math.Round(colors[i].GetBlue() * 255))
433 app.swatchCards[i].hex = hex 509 app.swatchCards[i].hex = hex
434 app.swatchCards[i].image.SetFromPixbuf(solidPixbuf(hex, 220, 150)) 510 app.swatchCards[i].image.SetFromPixbuf(solidPixbuf(hex, cardImageW, cardImageH))
435 app.swatchCards[i].label.SetText(fmt.Sprintf("%s\nrgb(%d, %d, %d)\nhsv(%d, %d, %d)", hex, r, g, b, int(h), int(s), int(v))) 511 app.swatchCards[i].label.SetText(fmt.Sprintf("%s\nrgb(%d, %d, %d)\nhsv(%d, %d, %d)", hex, r, g, b, int(h), int(s), int(v)))
436 } 512 }
437} 513}
@@ -530,6 +606,7 @@ func (app *App) updateActionStates() {
530 app.darkenBtn.SetSensitive(v > 5) 606 app.darkenBtn.SetSensitive(v > 5)
531 app.saturateBtn.SetSensitive(s < 100) 607 app.saturateBtn.SetSensitive(s < 100)
532 app.desaturateBtn.SetSensitive(s > 5) 608 app.desaturateBtn.SetSensitive(s > 5)
609 app.renameFavBtn.SetSensitive(app.selectedIter != nil)
533 app.removeFavBtn.SetSensitive(app.selectedIter != nil) 610 app.removeFavBtn.SetSensitive(app.selectedIter != nil)
534 app.clearFavBtn.SetSensitive(len(app.savedColors) > 0) 611 app.clearFavBtn.SetSensitive(len(app.savedColors) > 0)
535} 612}
@@ -550,19 +627,6 @@ func (app *App) activePaletteName() string {
550 return paletteNames[idx] 627 return paletteNames[idx]
551} 628}
552 629
553func (app *App) onPaletteSelectionChanged(selection *gtk.TreeSelection) {
554 model, iter, ok := selection.GetSelected()
555 if !ok {
556 return
557 }
558 value, _ := model.ToTreeModel().GetValue(iter, 1)
559 hex, _ := value.GetString()
560 rgba := gdk.NewRGBA()
561 if rgba.Parse(hex) {
562 app.setCurrentColor(rgba, true)
563 }
564}
565
566func (app *App) onFavoriteSelectionChanged(selection *gtk.TreeSelection) { 630func (app *App) onFavoriteSelectionChanged(selection *gtk.TreeSelection) {
567 model, iter, ok := selection.GetSelected() 631 model, iter, ok := selection.GetSelected()
568 if !ok { 632 if !ok {
@@ -580,12 +644,37 @@ func (app *App) onFavoriteSelectionChanged(selection *gtk.TreeSelection) {
580 app.updateActionStates() 644 app.updateActionStates()
581} 645}
582 646
583func (app *App) populatePaletteList() { 647func (app *App) populatePaletteGrid() {
584 app.paletteStore.Clear() 648 for {
585 for _, hex := range paletteByName(app.activePaletteName()) { 649 child, err := app.paletteGrid.GetChildAt(0, 0)
586 iter := app.paletteStore.Append() 650 if err != nil || child == nil {
587 app.paletteStore.Set(iter, []int{0, 1}, []interface{}{solidPixbuf(hex, 36, 14), hex}) 651 break
652 }
653 app.paletteGrid.Remove(child)
654 }
655
656 colors := paletteByName(app.activePaletteName())
657 cols := 24
658 for i, hex := range colors {
659 btn, _ := gtk.ButtonNew()
660 btn.SetRelief(gtk.RELIEF_NONE)
661 btn.SetCanFocus(false)
662 btn.SetSizeRequest(16, 11)
663 if ctx, err := btn.GetStyleContext(); err == nil {
664 ctx.AddClass("palette-swatch")
665 }
666 img, _ := gtk.ImageNewFromPixbuf(solidPixbuf(hex, 16, 11))
667 btn.Add(img)
668 h := hex
669 btn.Connect("clicked", func() {
670 rgba := gdk.NewRGBA()
671 if rgba.Parse(h) {
672 app.setCurrentColor(rgba, true)
673 }
674 })
675 app.paletteGrid.Attach(btn, i%cols, i/cols, 1, 1)
588 } 676 }
677 app.paletteGrid.ShowAll()
589} 678}
590 679
591func (app *App) addCurrentToFavorites() { 680func (app *App) addCurrentToFavorites() {
@@ -616,6 +705,54 @@ func (app *App) removeSelectedFavorite() {
616 app.saveConfig() 705 app.saveConfig()
617} 706}
618 707
708func (app *App) renameSelectedFavorite() {
709 if app.selectedIter == nil {
710 return
711 }
712 vHex, _ := app.favoritesStore.GetValue(app.selectedIter, 1)
713 hex, _ := vHex.GetString()
714 vName, _ := app.favoritesStore.GetValue(app.selectedIter, 2)
715 currentName, _ := vName.GetString()
716
717 dialog, _ := gtk.DialogNew()
718 dialog.SetTitle("Rename Color")
719 dialog.SetTransientFor(app.window)
720 dialog.SetModal(true)
721 box, _ := dialog.GetContentArea()
722 box.SetMarginTop(10)
723 box.SetMarginBottom(10)
724 box.SetMarginStart(10)
725 box.SetMarginEnd(10)
726 box.SetSpacing(6)
727 label, _ := gtk.LabelNew("Enter a new name:")
728 label.SetHAlign(gtk.ALIGN_START)
729 box.PackStart(label, false, false, 0)
730 entry, _ := gtk.EntryNew()
731 entry.SetText(currentName)
732 entry.SetActivatesDefault(true)
733 box.PackStart(entry, false, false, 0)
734 dialog.AddButton("Cancel", gtk.RESPONSE_CANCEL)
735 okBtn, _ := dialog.AddButton("OK", gtk.RESPONSE_OK)
736 okBtn.SetCanDefault(true)
737 okBtn.GrabDefault()
738 dialog.ShowAll()
739 if dialog.Run() == gtk.RESPONSE_OK {
740 newName, _ := entry.GetText()
741 newName = strings.TrimSpace(newName)
742 if newName != "" {
743 for i := range app.savedColors {
744 if strings.EqualFold(app.savedColors[i].Hex, hex) {
745 app.savedColors[i].Name = newName
746 break
747 }
748 }
749 app.refreshFavoritesView()
750 app.saveConfig()
751 }
752 }
753 dialog.Destroy()
754}
755
619func (app *App) refreshFavoritesView() { 756func (app *App) refreshFavoritesView() {
620 app.favoritesStore.Clear() 757 app.favoritesStore.Clear()
621 for _, color := range app.savedColors { 758 for _, color := range app.savedColors {
@@ -625,19 +762,6 @@ func (app *App) refreshFavoritesView() {
625 app.updateActionStates() 762 app.updateActionStates()
626} 763}
627 764
628func (app *App) exportFavoritesGPLToDefaultPath() {
629 if len(app.savedColors) == 0 {
630 return
631 }
632 outPath := filepath.Join(filepath.Dir(app.configFile), "kjagave-favorites.gpl")
633 lines := []string{"GIMP Palette", "Name: kjagave Favorites", "#"}
634 for _, item := range app.savedColors {
635 r, g, b := hexToRGB(item.Hex)
636 lines = append(lines, fmt.Sprintf("%3d %3d %3d\t%s", r, g, b, item.Name))
637 }
638 _ = os.WriteFile(outPath, []byte(strings.Join(lines, "\n")+"\n"), 0644)
639}
640
641func (app *App) onAboutClicked() { 765func (app *App) onAboutClicked() {
642 dialog, _ := gtk.AboutDialogNew() 766 dialog, _ := gtk.AboutDialogNew()
643 dialog.SetTransientFor(app.window) 767 dialog.SetTransientFor(app.window)
@@ -926,19 +1050,6 @@ func rgbaToHex(rgba *gdk.RGBA) string {
926 return fmt.Sprintf("#%02X%02X%02X", r, g, b) 1050 return fmt.Sprintf("#%02X%02X%02X", r, g, b)
927} 1051}
928 1052
929func hexToRGB(hex string) (int, int, int) {
930 text := strings.TrimPrefix(strings.TrimSpace(hex), "#")
931 if len(text) != 6 {
932 return 0, 0, 0
933 }
934 var r, g, b int
935 _, err := fmt.Sscanf(text, "%02x%02x%02x", &r, &g, &b)
936 if err != nil {
937 return 0, 0, 0
938 }
939 return r, g, b
940}
941
942func wrapHue(h float64) float64 { 1053func wrapHue(h float64) float64 {
943 v := math.Mod(h, 360) 1054 v := math.Mod(h, 360)
944 if v < 0 { 1055 if v < 0 {