diff options
| -rw-r--r-- | src/main.go | 319 |
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 | ||
| 27 | type SavedColor struct { | 29 | type SavedColor struct { |
| @@ -45,6 +47,7 @@ type SwatchCard struct { | |||
| 45 | 47 | ||
| 46 | type App struct { | 48 | type 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 | ||
| 383 | func (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 | |||
| 430 | func (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 | |||
| 439 | func (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 | |||
| 374 | func (app *App) newSwatchCard() SwatchCard { | 448 | func (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 | ||
| 553 | func (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 | |||
| 566 | func (app *App) onFavoriteSelectionChanged(selection *gtk.TreeSelection) { | 630 | func (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 | ||
| 583 | func (app *App) populatePaletteList() { | 647 | func (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 | ||
| 591 | func (app *App) addCurrentToFavorites() { | 680 | func (app *App) addCurrentToFavorites() { |
| @@ -616,6 +705,54 @@ func (app *App) removeSelectedFavorite() { | |||
| 616 | app.saveConfig() | 705 | app.saveConfig() |
| 617 | } | 706 | } |
| 618 | 707 | ||
| 708 | func (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 | |||
| 619 | func (app *App) refreshFavoritesView() { | 756 | func (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 | ||
| 628 | func (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 | |||
| 641 | func (app *App) onAboutClicked() { | 765 | func (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 | ||
| 929 | func 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 | |||
| 942 | func wrapHue(h float64) float64 { | 1053 | func wrapHue(h float64) float64 { |
| 943 | v := math.Mod(h, 360) | 1054 | v := math.Mod(h, 360) |
| 944 | if v < 0 { | 1055 | if v < 0 { |
