├── .gitignore ├── Makefile ├── resources ├── emma.png ├── override.css ├── placeholder.png ├── icons │ ├── copy-button.svg │ └── color-picker-button.svg ├── index.xml └── app.css ├── binaries └── colr_1.0-1_amd64.deb ├── shell.nix ├── src ├── ui │ ├── editor.h │ ├── ui.h │ ├── about.c │ ├── editor.c │ ├── ui.c │ ├── editor-rgb.c │ ├── editor-hsv.c │ └── init.c ├── util.h ├── app.h ├── color │ ├── color.h │ └── color.c ├── app.c └── util.c ├── meson.build ├── compile_flags.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | resources/*.c 2 | .cache 3 | resources/*.h 4 | build 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | meson builddir 3 | meson compile -C builddir 4 | -------------------------------------------------------------------------------- /resources/emma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidnoronha1/colr/HEAD/resources/emma.png -------------------------------------------------------------------------------- /resources/override.css: -------------------------------------------------------------------------------- 1 | *:selected, selection { 2 | color: white; 3 | background-color: blue; 4 | } 5 | -------------------------------------------------------------------------------- /resources/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidnoronha1/colr/HEAD/resources/placeholder.png -------------------------------------------------------------------------------- /binaries/colr_1.0-1_amd64.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidnoronha1/colr/HEAD/binaries/colr_1.0-1_amd64.deb -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | with pkgs; 4 | 5 | mkShell { 6 | buildInputs = [ pkg-config gtk3 xorg.libX11 ]; 7 | } 8 | -------------------------------------------------------------------------------- /src/ui/editor.h: -------------------------------------------------------------------------------- 1 | #ifndef EDITOR_H 2 | #define EDITOR_H 3 | 4 | #include "../util.h" 5 | #include "ui.h" 6 | GtkWidget *label_scale(GtkWidget *scale, const char* label); 7 | void color_edit_menu(UNUSED GtkWidget *self, struct CallbackData *cbd); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/ui/ui.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_H 2 | #define UI_H 3 | #include "../app.h" 4 | 5 | void apply_style (GtkWidget *w, char*style); 6 | void show_color(UNUSED GtkWidget *widget, struct CallbackData *data); 7 | void add_new_color(struct CallbackData *data); 8 | /* GtkWidget *create_color_row(const char *label, GtkWidget *panel); */ 9 | /* GtkWidget *create_menu(struct CallbackData *ui); */ 10 | void remove_current_color(UNUSED GtkWidget*self, struct CallbackData *data); 11 | STATE init_ui(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /resources/icons/copy-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | placeholder.png 5 | emma.png 6 | app.css 7 | override.css 8 | 9 | 10 | icons/color-picker-button.svg 11 | icons/copy-button.svg 12 | 13 | 14 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('colr', 'c', 2 | default_options : [ ]) 3 | 4 | # Add Math Library 5 | cc = meson.get_compiler('c') 6 | 7 | deps = [ dependency('gtk+-3.0'), dependency('x11'), cc.find_library('m', required: false) ] 8 | 9 | gnome = import('gnome') 10 | resources = gnome.compile_resources( 11 | 'colr-resources', 12 | 'resources/index.xml', 13 | source_dir: 'resources', 14 | c_name: 'colr' 15 | ) 16 | 17 | src = [ 18 | 'src/app.c', 19 | 'src/util.c', 20 | 'src/ui/ui.c', 21 | 'src/color/color.c', 22 | 'src/ui/init.c', 23 | 'src/ui/editor-hsv.c', 24 | 'src/ui/editor-rgb.c', 25 | 'src/ui/editor.c', 26 | 'src/ui/about.c', 27 | resources 28 | ] 29 | 30 | executable('colr', sources : src, dependencies : deps) 31 | 32 | -------------------------------------------------------------------------------- /compile_flags.txt: -------------------------------------------------------------------------------- 1 | -pthread 2 | -I/usr/include/gtk-3.0 3 | -I/usr/include/at-spi2-atk/2.0 4 | -I/usr/include/at-spi-2.0 5 | -I/usr/include/dbus-1.0 6 | -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include 7 | -I/usr/include/gtk-3.0 8 | -I/usr/include/gio-unix-2.0 9 | -I/usr/include/cairo 10 | -I/usr/include/pango-1.0 11 | -I/usr/include/fribidi 12 | -I/usr/include/harfbuzz 13 | -I/usr/include/atk-1.0 14 | -I/usr/include/cairo 15 | -I/usr/include/pixman-1 16 | -I/usr/include/uuid 17 | -I/usr/include/freetype2 18 | -I/usr/include/libpng16 19 | -I/usr/include/gdk-pixbuf-2.0 20 | -I/usr/include/libmount 21 | -I/usr/include/blkid 22 | -I/usr/include/glib-2.0 23 | -I/usr/lib/x86_64-linux-gnu/glib-2.0/include 24 | -lgtk-3 25 | -lgdk-3 26 | -lpangocairo-1.0 27 | -lpango-1.0 28 | -lharfbuzz 29 | -latk-1.0 30 | -lcairo-gobject 31 | -lcairo 32 | -lgdk_pixbuf-2.0 33 | -lgio-2.0 34 | -lgobject-2.0 35 | -lglib-2.0 36 | -lX11 37 | -------------------------------------------------------------------------------- /src/ui/about.c: -------------------------------------------------------------------------------- 1 | #include "../app.h" 2 | 3 | void about_dialog(UNUSED GtkWidget* self, GtkWidget * parent) { 4 | GtkWidget *diag = gtk_dialog_new_with_buttons("ABOUT", GTK_WINDOW(parent), 0, "OK", GTK_RESPONSE_ACCEPT, NULL); 5 | 6 | GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(diag)); 7 | GtkWidget *emma = gtk_image_new_from_resource("/undefinedDarkness/colr/emma.png"); 8 | GtkWidget *text = gtk_label_new(""); 9 | gtk_label_set_markup(GTK_LABEL(text), "COLR: A simple color picker built in C with GTK for X11\nAuthor: undefinedDarkness"); 10 | GtkWidget *link = gtk_link_button_new_with_label("https://github.com/undefinedDarkness/colr", "Respository"); 11 | 12 | gtk_container_add(GTK_CONTAINER(area), emma); 13 | gtk_container_add(GTK_CONTAINER(area), text); 14 | gtk_container_add(GTK_CONTAINER(area), link); 15 | gtk_box_set_spacing(GTK_BOX(area), 16); 16 | 17 | gtk_widget_show_all(area); 18 | gtk_dialog_run(GTK_DIALOG(diag)); 19 | gtk_widget_destroy(diag); 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | A simple color picker GUI for X11. 4 |

5 | 6 | ## Motivation 7 | I just needed something pretty for my rice and I didn't want to pull out a terminal app and I really liked the design of M$ PowerToys's Colour Picker so I created this, It ain't perfect but it's good enough in my opinion 8 | 9 | ## Install & Usage 10 | Obtain the binary package from the `./binaries/` folder: 11 | To install the deb package, run `sudo apt install ./*.deb` 12 | To use it, run: `colr` 13 | 14 | ## Building 15 | 16 | ```sh 17 | $ git clone https://github.com/undefinedDarkness/colr.git 18 | $ cd colr 19 | $ meson build 20 | $ cd build && meson compile 21 | $ mv colr ~/.local/bin # Move to path 22 | ``` 23 | 24 | Building Requirements: 25 | - Gtk3 Development Headers (libgtk-3-dev) 26 | - X11 Development Headers (libx11-dev) 27 | - Meson & Ninja 28 | 29 | ## Todo 30 | - Extend HSV Support 31 | - Fix Clipboard Persistence - Not easily fixed 32 | - Restyle About Dialog 33 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | #include "app.h" 4 | 5 | void paste_color_to_clipboard(UNUSED GtkWidget* parent, struct Color *c); 6 | void button_cursor(GtkWidget *btn, char*cursor);void paste_label_to_clipboard(UNUSED GtkWidget* parent, GtkWidget *source); 7 | void paste_label_to_clipboard(UNUSED GtkWidget* parent, GtkWidget *source);int starts_with(char *check, char *full); 8 | void on_file_drop( 9 | UNUSED GtkWidget *self, 10 | UNUSED GdkDragContext *ctx, 11 | UNUSED int x, UNUSED int y, 12 | GtkSelectionData *sel, 13 | UNUSED int info, 14 | UNUSED int time, 15 | struct CallbackData *ui); 16 | void free_2nd(UNUSED GtkWidget *widget, struct CallbackData *data);void attach_menu(UNUSED GtkWidget* self, GdkEvent *event, GtkWidget *menu); 17 | void remove_current_color(UNUSED GtkWidget*self, struct CallbackData *data); 18 | void save_to_disk(UNUSED GtkWidget*self, GtkWidget*sidebar); 19 | void parse_colors_from_file(const char* path, struct CallbackData *ui); 20 | 21 | extern char modB[100]; // buffer shared for use within functions, note: never cleaned 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/app.h: -------------------------------------------------------------------------------- 1 | #ifndef APP_INCLUDED 2 | #define APP_INCLUDED 3 | 4 | /* -- IMPORTS -- */ 5 | #include 6 | #include "color/color.h" 7 | #include 8 | #include 9 | 10 | /* -- MACROS -- */ 11 | #define UNUSED __attribute__((unused)) 12 | #define SZ(arr) sizeof(arr)/sizeof(arr[0]) 13 | 14 | // Shameful 15 | #define BOX gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0) 16 | 17 | /* -- STRUCTURES -- */ 18 | 19 | // TODO: This structure is like a sticky goo infecting everything 20 | // I know its dumb, but I am really too lazy to fix it 21 | 22 | struct UIData { 23 | GtkWidget *sidebar; 24 | GtkWidget *panel; 25 | GtkWidget *picker; 26 | GtkWidget *window; 27 | 28 | GtkWidget *rgb_display; 29 | GtkWidget *hex_display; 30 | GtkWidget *hsv_display; 31 | }; 32 | typedef struct UIData UI; 33 | 34 | struct CallbackData { 35 | UI* UI; 36 | GtkWidget *color; 37 | GtkWidget *color_light; 38 | GtkWidget *color_dark; 39 | 40 | struct Color color_data; // I am not entirely sure why we have this 41 | }; 42 | typedef struct CallbackData STATE; 43 | 44 | enum FileTypes { 45 | FT_PLAINTEXT, 46 | FT_URILIST 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/color/color.h: -------------------------------------------------------------------------------- 1 | typedef struct _GtkWidget GtkWidget; 2 | 3 | struct Color { 4 | int r; 5 | int g; 6 | int b; 7 | }; 8 | 9 | typedef struct Color Color; 10 | 11 | enum ColorChannel { COLOR_CHANNEL_RED, COLOR_CHANNEL_GREEN, COLOR_CHANNEL_BLUE }; 12 | 13 | struct ColorHSV { 14 | double hue; 15 | double saturation; 16 | double value; 17 | }; 18 | 19 | typedef struct ColorHSV ColorHSV; 20 | ColorHSV color_hsv(Color); 21 | Color color_hsv_re(ColorHSV); 22 | 23 | // Misc 24 | int color_pick (struct Color *color); 25 | struct Color color_apply (struct Color *c, int amount); 26 | /* char *create_color_range_gradient(struct Color c, enum ColorChannel, char*buffer); */ 27 | struct Color color_from_hex(char *hex); 28 | void color_get_dominant(const char*path, struct Color *c); 29 | 30 | // Color Conversion Functions 31 | void color_to_hex(struct Color color, char*space); 32 | void color_to_rgb(struct Color color, char*space); 33 | void color_to_hsv(struct Color color, char*space); 34 | 35 | // TODO: Move to UI 36 | // Get & Set Widget Background 37 | void color_set_bg(struct Color *color_data, GtkWidget *widget); 38 | struct Color color_get_bg(GtkWidget *self); 39 | -------------------------------------------------------------------------------- /resources/icons/color-picker-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/ui/editor.c: -------------------------------------------------------------------------------- 1 | #include "editor.h" 2 | 3 | void rgb_editor(GtkWidget*, STATE *cbd); 4 | void hsv_editor(GtkWidget*, STATE *cbd); 5 | 6 | GtkWidget *label_scale(GtkWidget *scale, const char* label) { 7 | GtkWidget * container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); 8 | GtkWidget *lw = gtk_label_new(label); 9 | gtk_label_set_yalign(GTK_LABEL(lw), 0.7); 10 | gtk_box_pack_start(GTK_BOX(container), lw, 0, 0, 16); 11 | gtk_box_pack_start(GTK_BOX(container), scale, 1, 1, 0); 12 | return container; 13 | } 14 | 15 | 16 | void color_edit_menu(UNUSED GtkWidget *self, STATE *state) { 17 | 18 | GtkWidget *dialog = gtk_dialog_new(); 19 | gtk_window_set_default_size(GTK_WINDOW(dialog), 250, 150); 20 | 21 | GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 22 | gtk_style_context_add_class(gtk_widget_get_style_context(area), "no-padding"); 23 | 24 | GtkWidget *notebook = gtk_notebook_new(); 25 | 26 | rgb_editor(notebook, state); 27 | hsv_editor(notebook, state); 28 | 29 | gtk_box_set_homogeneous(GTK_BOX(area), FALSE); 30 | gtk_box_pack_start(GTK_BOX(area), notebook, 0, 1, 0); 31 | gtk_widget_show_all(area); 32 | 33 | gtk_dialog_run(GTK_DIALOG(dialog)); 34 | } 35 | -------------------------------------------------------------------------------- /resources/app.css: -------------------------------------------------------------------------------- 1 | .sidebar > * { 2 | margin: 8px; 3 | } 4 | 5 | .color-display *, .sidebar > *:not(:first-child) { 6 | background: none; 7 | background-image: none; 8 | box-shadow: 0 0; 9 | } 10 | 11 | .color-display * { 12 | margin: 0; 13 | border: none; 14 | box-shadow: none; 15 | border-radius: 0; 16 | min-height: 40px; 17 | } 18 | 19 | .color-display *:first-child { 20 | border-radius: 5px 0 0 5px; 21 | } 22 | 23 | .color-display *:last-child { 24 | border-radius: 0 5px 5px 0; 25 | } 26 | 27 | .color-display { 28 | margin-bottom: 16px; 29 | } 30 | 31 | .color-preview { 32 | margin: 16px; 33 | } 34 | 35 | frame { 36 | margin: 5px; 37 | } 38 | 39 | .color-row { 40 | padding: 4px 4px 4px 10px; 41 | } 42 | 43 | .color-row *:first-child { 44 | font-family: monospace; 45 | } 46 | 47 | .color-row *:last-child { 48 | border: 0; 49 | background: none; 50 | box-shadow: none; 51 | } 52 | 53 | .dialog-action-box { 54 | margin-top: 20px; 55 | } 56 | 57 | .dialog-vbox { 58 | margin: 20px; 59 | } 60 | 61 | scale trough highlight { 62 | background-color: transparent; 63 | } 64 | 65 | scale slider { 66 | padding: 3px; 67 | } 68 | 69 | .no-padding { 70 | padding: 0; 71 | margin: 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/app.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | #include "util.h" 3 | #include "ui/ui.h" 4 | #include "ui/editor.h" 5 | 6 | static void add_new_color_from_pick(UNUSED GtkWidget *self, struct CallbackData *ui) { 7 | if (color_pick(&ui->color_data) == -1) { 8 | return; 9 | } 10 | add_new_color(ui); 11 | } 12 | 13 | static void add_new_color_from_widget(GtkWidget *self, struct CallbackData *ui) { 14 | ui->color_data = color_get_bg(self); 15 | add_new_color(ui); 16 | } 17 | 18 | /* -- Init Subroutines {{{ */ 19 | 20 | GResource *colr_get_resource(); 21 | static void init_resource() { 22 | GResource *res = colr_get_resource(); 23 | g_resources_register(res); 24 | 25 | GtkIconTheme *theme = gtk_icon_theme_get_default(); 26 | gtk_icon_theme_add_resource_path (theme, "/undefinedDarkness/colr/icons"); 27 | } 28 | 29 | static void init_styling() { 30 | GtkCssProvider *provider = gtk_css_provider_new(); 31 | gtk_css_provider_load_from_resource(provider, "/undefinedDarkness/colr/app.css"); // Loaded from resources file. 32 | GdkScreen *display = gdk_display_get_default_screen(gdk_display_get_default()); 33 | gtk_style_context_add_provider_for_screen(display, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 34 | 35 | GtkCssProvider *override_provider = gtk_css_provider_new(); 36 | gtk_css_provider_load_from_resource(override_provider, "/undefinedDarkness/colr/override.css"); // Loaded from resources file. 37 | gtk_style_context_add_provider_for_screen(display, GTK_STYLE_PROVIDER(override_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); 38 | } 39 | 40 | /* }}} */ 41 | 42 | int main(int argc, char ** argv) { 43 | // Initialize GTK 44 | gtk_init(&argc, &argv); 45 | 46 | init_resource(); 47 | 48 | // Build User Interface 49 | STATE state = init_ui(); 50 | init_styling(); 51 | 52 | // Attack Signals 53 | g_signal_connect(G_OBJECT(state.color), "clicked", G_CALLBACK(color_edit_menu), &state); 54 | g_signal_connect(G_OBJECT(state.color_light), "clicked", G_CALLBACK(add_new_color_from_widget), &state); 55 | g_signal_connect(G_OBJECT(state.color_dark), "clicked", G_CALLBACK(add_new_color_from_widget), &state); 56 | g_signal_connect(G_OBJECT(state.UI->picker), "clicked", G_CALLBACK(add_new_color_from_pick), &state); 57 | 58 | // Show Windows 59 | gtk_widget_show_all(state.UI->window); 60 | 61 | // Start Runtime 62 | gtk_main(); 63 | } 64 | -------------------------------------------------------------------------------- /src/ui/ui.c: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | #include "../util.h" 3 | 4 | void apply_style (GtkWidget *w, char*style) { 5 | GtkCssProvider *css = gtk_css_provider_new(); 6 | gtk_css_provider_load_from_data(css, style, strlen(style), NULL); 7 | gtk_style_context_add_provider(gtk_widget_get_style_context(w) ,GTK_STYLE_PROVIDER(css), 600); 8 | } 9 | 10 | void show_color(UNUSED GtkWidget *widget, struct CallbackData *data) { 11 | struct Color color_light = color_apply(&data->color_data, 25); 12 | struct Color color_dark = color_apply(&data->color_data, -25); 13 | 14 | 15 | /* Have no clue what this code does */ 16 | /* GList *children = gtk_container_get_children(GTK_CONTAINER(data->UI->sidebar)); */ 17 | /* GList* head = children; */ 18 | /* children=children->next; // Skip picker button */ 19 | /* struct Color previous = color_get_bg(data->color); */ 20 | /* while (children != NULL) { */ 21 | /* struct Color c = color_get_bg(children->data); */ 22 | /* if (c.r == previous.r && c.g == previous.g && c.b == previous.b) { */ 23 | /* color_set_bg(&data->color_data, children->data); */ 24 | /* break; */ 25 | /* } */ 26 | /* children = children->next; */ 27 | /* } */ 28 | /* g_list_free(head); */ 29 | 30 | color_set_bg(&data->color_data, data->color); 31 | color_set_bg(&color_light, data->color_light); 32 | color_set_bg(&color_dark, data->color_dark); 33 | 34 | color_to_rgb(data->color_data, modB); 35 | gtk_label_set_text(GTK_LABEL(data->UI->rgb_display), modB); 36 | 37 | color_to_hsv(data->color_data, modB); 38 | gtk_label_set_text(GTK_LABEL(data->UI->hsv_display), modB); 39 | 40 | color_to_hex(data->color_data, modB); 41 | gtk_label_set_text(GTK_LABEL(data->UI->hex_display), modB); 42 | 43 | gtk_widget_show_all(data->UI->panel); 44 | /* free(color_text); */ 45 | } 46 | 47 | void add_new_color(struct CallbackData *data) { 48 | // Check for duplicate colors 49 | GList *children = gtk_container_get_children(GTK_CONTAINER(data->UI->sidebar)); 50 | GList* head = children; 51 | children=children->next; // Skip picker button 52 | while (children != NULL) { 53 | struct Color c = color_get_bg(children->data); 54 | if (c.r == data->color_data.r && c.g == data->color_data.g && c.b == data->color_data.b) { 55 | gtk_button_clicked(children->data); 56 | goto end; 57 | } 58 | children = children->next; 59 | } 60 | g_list_free(head); 61 | 62 | 63 | GtkWidget *button = gtk_button_new(); 64 | 65 | struct CallbackData *copy = malloc(sizeof (struct CallbackData)); 66 | color_set_bg(&data->color_data, button); 67 | 68 | // Create a copy of the struct to preserve the data 69 | memcpy(copy, data, sizeof (struct CallbackData)); 70 | g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(show_color), copy); 71 | g_signal_connect(G_OBJECT(button), "destroy", G_CALLBACK(free_2nd), copy); // FREED HERE 72 | gtk_widget_show_all(button); 73 | 74 | // Switch to the new color 75 | show_color(NULL, copy); 76 | gtk_container_add(GTK_CONTAINER(data->UI->sidebar), button); 77 | button_cursor(button, NULL); 78 | 79 | end: 80 | g_list_free(children); 81 | } 82 | 83 | 84 | 85 | 86 | 87 | 88 | void remove_current_color(UNUSED GtkWidget*self, struct CallbackData *data) { 89 | GList *children = gtk_container_get_children(GTK_CONTAINER(data->UI->sidebar)); 90 | struct Color ch = color_get_bg(data->color); 91 | children = children->next; // Skip picker button 92 | while (children != NULL) { 93 | struct Color c = color_get_bg(children->data); 94 | if (ch.r == c.r && ch.g == c.g && ch.b == c.b) { 95 | gtk_widget_destroy(children->data); 96 | children = children->next; 97 | if (children == NULL) { 98 | children = g_list_first(children); 99 | } 100 | gtk_button_clicked(children->data); 101 | break; 102 | } 103 | children = children->next; 104 | } 105 | g_list_free(children); 106 | } 107 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "ui/ui.h" 3 | #include 4 | 5 | 6 | char modB[100]; 7 | 8 | void paste_color_to_clipboard(UNUSED GtkWidget* parent, struct Color *c) { 9 | GtkClipboard* clip = gtk_clipboard_get_default(gdk_display_get_default()); 10 | color_to_hex(*c, modB); 11 | gtk_clipboard_set_text(clip, modB, 8); 12 | } 13 | 14 | void button_cursor(GtkWidget *btn, char*cursor) { 15 | gdk_window_set_cursor( 16 | gtk_button_get_event_window(GTK_BUTTON(btn)), 17 | gdk_cursor_new_from_name(gdk_display_get_default(), cursor == NULL ? "pointer" : cursor)); 18 | } 19 | 20 | void paste_label_to_clipboard(UNUSED GtkWidget* parent, GtkWidget *source) { 21 | GtkClipboard* clip = gtk_clipboard_get_default(gdk_display_get_default()); 22 | const char *to_paste = gtk_label_get_text(GTK_LABEL(source)); 23 | gtk_clipboard_set_text(clip, to_paste, -1); 24 | } 25 | 26 | // Simply frees the 2nd item it gets 27 | void free_2nd(UNUSED GtkWidget *widget, struct CallbackData *data) { 28 | free(data); 29 | } 30 | 31 | // See: https://www.cc.gatech.edu/data_files/public/doc/gtk/tutorial/gtk_tut-14.html 32 | void attach_menu(UNUSED GtkWidget* self, GdkEvent *event, GtkWidget *menu) { 33 | if (event->type == GDK_BUTTON_PRESS) { 34 | GdkEventButton *event_button; 35 | event_button = (GdkEventButton *)event; 36 | if (event_button->button == GDK_BUTTON_SECONDARY) { 37 | gtk_menu_popup_at_pointer(GTK_MENU(menu), event); 38 | } 39 | } 40 | } 41 | 42 | 43 | 44 | // This function is the opposite of tiny... 45 | void save_to_disk(UNUSED GtkWidget*self, GtkWidget*sidebar) { 46 | GtkWidget *chooser = gtk_file_chooser_dialog_new( 47 | "Save As", 48 | NULL, 49 | GTK_FILE_CHOOSER_ACTION_SAVE, 50 | "Cancel", 51 | GTK_RESPONSE_CANCEL, 52 | "Save", 53 | GTK_RESPONSE_ACCEPT, 54 | NULL); 55 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE); 56 | /* gtk_window_resize(GTK_WINDOW(chooser), 300, 200); */ 57 | if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) { 58 | char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); 59 | 60 | // Get all the children 61 | GList *children = gtk_container_get_children(GTK_CONTAINER(sidebar)); 62 | children=children->next; // Skip picker button 63 | FILE *file = fopen(filename, "w"); 64 | while (children != NULL) { 65 | struct Color c = color_get_bg(children->data); 66 | fprintf(file, "#%02x%02x%02x\n", c.r, c.g, c.b); 67 | children = children->next; 68 | } 69 | 70 | // Cleanup 71 | g_list_free(children); 72 | free(filename); 73 | fclose(file); 74 | } 75 | gtk_widget_destroy(chooser); 76 | } 77 | 78 | void parse_colors_from_file(const char* path, struct CallbackData *ui) { 79 | /* g_print("parsing from: '%s'\n", path); */ 80 | FILE *file = fopen(path, "r"); 81 | while (fgets(modB, 9, file)) { 82 | modB[7] = '\0'; // strip newline 83 | ui->color_data = color_from_hex(modB); 84 | add_new_color(ui); 85 | /* g_print("%s", buf); */ 86 | } 87 | fclose(file); 88 | } 89 | 90 | 91 | 92 | void on_file_drop( 93 | UNUSED GtkWidget *self, 94 | UNUSED GdkDragContext *ctx, 95 | UNUSED int x, UNUSED int y, 96 | GtkSelectionData *sel, 97 | UNUSED int info, 98 | UNUSED int time, 99 | struct CallbackData *ui) { 100 | char* type = gdk_atom_name(gtk_selection_data_get_data_type(sel)); 101 | 102 | if (strcmp(type, "text/uri-list") == 0) { 103 | char **uris = gtk_selection_data_get_uris(sel); 104 | const char* path = g_uri_get_path(g_uri_parse(uris[0], 0, NULL)); 105 | char *ft = g_content_type_guess(path, NULL, 0, NULL); 106 | if (strcmp(ft, "application/octet-stream") == 0 || strcmp(ft, "text/plain") == 0) { 107 | parse_colors_from_file(path, ui); 108 | } 109 | else if (strncmp("image/", ft, sizeof("image/")) == 0) { 110 | struct Color c; 111 | color_get_dominant(path, &c); 112 | ui->color_data = c; 113 | add_new_color(ui); 114 | } 115 | else { 116 | g_warning("Unknown filetype: %s", ft); 117 | } 118 | free(uris); 119 | } 120 | 121 | g_free(type); 122 | } 123 | -------------------------------------------------------------------------------- /src/ui/editor-rgb.c: -------------------------------------------------------------------------------- 1 | #include "editor.h" 2 | 3 | // Internal State Structure. 4 | struct EditorCB { 5 | GtkWidget *scale_r; 6 | GtkWidget *scale_g; 7 | GtkWidget *scale_b; 8 | struct CallbackData *cb; 9 | }; 10 | 11 | // TODO: 12 | // Since most of the format stays the same, Why recreate it every time 13 | static char *create_color_range_gradient(struct Color c, enum ColorChannel channel) { 14 | switch(channel) { 15 | case COLOR_CHANNEL_RED: 16 | sprintf(modB, "trough{background-image:linear-gradient(to right,#00%02x%02x,#ff%02x%02x);}", c.g, c.b, c.g, c.b); 17 | break; 18 | case COLOR_CHANNEL_BLUE: 19 | sprintf(modB, "trough{background-image:linear-gradient(to right,#%02x%02x00,#%02x%02xff);}", c.r, c.g, c.r, c.g); 20 | break; 21 | case COLOR_CHANNEL_GREEN: 22 | sprintf(modB, "trough{background-image:linear-gradient(to right,#%02x00%02x,#%02xff%02x);}", c.r, c.b, c.r, c.b); 23 | break; 24 | } 25 | return modB; 26 | } 27 | 28 | 29 | static void update_r(GtkWidget *self, struct EditorCB *ui) { 30 | ui->cb->color_data.r = gtk_range_get_value(GTK_RANGE(self)); 31 | 32 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_GREEN); 33 | apply_style(ui->scale_g, modB); 34 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_BLUE); 35 | apply_style(ui->scale_b, modB); 36 | 37 | show_color(NULL, ui->cb); 38 | }; 39 | 40 | static void update_g(GtkWidget *self, struct EditorCB *ui) { 41 | 42 | ui->cb->color_data.g = gtk_range_get_value(GTK_RANGE(self)); 43 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_RED); 44 | apply_style(ui->scale_r, modB); 45 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_BLUE); 46 | apply_style(ui->scale_b, modB); 47 | 48 | show_color(NULL, ui->cb); 49 | }; 50 | 51 | static void update_b(GtkWidget *self, struct EditorCB *ui) { 52 | ui->cb->color_data.b = gtk_range_get_value(GTK_RANGE(self)); 53 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_RED); 54 | apply_style(ui->scale_r, modB); 55 | create_color_range_gradient(ui->cb->color_data, COLOR_CHANNEL_GREEN); 56 | apply_style(ui->scale_g, modB); 57 | 58 | show_color(NULL, ui->cb); 59 | }; 60 | 61 | static void update_from_src(UNUSED GtkWidget* parent, GtkWidget* self, UNUSED int index, struct EditorCB *ui) { 62 | GtkWidget *area = gtk_widget_get_parent(gtk_widget_get_parent(ui->scale_r)); 63 | 64 | if (area == self) { 65 | Color c = ui->cb->color_data; 66 | gtk_range_set_value(GTK_RANGE(ui->scale_r), c.r); 67 | gtk_range_set_value(GTK_RANGE(ui->scale_g), c.g); 68 | gtk_range_set_value(GTK_RANGE(ui->scale_b), c.b); 69 | } 70 | 71 | } 72 | 73 | void rgb_editor(GtkWidget*nb, STATE *cbd) { 74 | struct EditorCB *ui = malloc(sizeof(struct EditorCB)); 75 | ui->cb = cbd; 76 | Color c = color_get_bg(cbd->color); // Get starting color 77 | cbd->color_data = c; 78 | 79 | GtkWidget* content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); 80 | 81 | // TODO: Look into using the gtk color scale widget 82 | ui->scale_r = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 255, 5); 83 | gtk_range_set_value(GTK_RANGE(ui->scale_r), c.r); 84 | ui->scale_g = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 255, 5); 85 | gtk_range_set_value(GTK_RANGE(ui->scale_g), c.g); 86 | ui->scale_b = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 255, 5); 87 | gtk_range_set_value(GTK_RANGE(ui->scale_b), c.b); 88 | 89 | update_r(ui->scale_r, ui); update_g(ui->scale_g, ui); update_b(ui->scale_b, ui); 90 | g_signal_connect(G_OBJECT(ui->scale_r), "value-changed", G_CALLBACK(update_r), ui); 91 | g_signal_connect(G_OBJECT(ui->scale_g), "value-changed", G_CALLBACK(update_g), ui); 92 | g_signal_connect(G_OBJECT(ui->scale_b), "value-changed", G_CALLBACK(update_b), ui); 93 | 94 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->scale_r, "Red")); 95 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->scale_g, "Green")); 96 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->scale_b, "Blue")); 97 | 98 | g_signal_connect(G_OBJECT(nb), "switch-page", G_CALLBACK(update_from_src), ui); 99 | gtk_notebook_append_page(GTK_NOTEBOOK(nb), content, gtk_label_new("RGB")); 100 | } 101 | -------------------------------------------------------------------------------- /src/ui/editor-hsv.c: -------------------------------------------------------------------------------- 1 | #include "editor.h" 2 | 3 | static char *create_color_range_gradient(ColorHSV a, ColorHSV b) { 4 | Color c1 = color_hsv_re(a); 5 | Color c2 = color_hsv_re(b); 6 | sprintf(modB, "trough{background-image:linear-gradient(to right,rgb(%d,%d,%d),rgb(%d,%d,%d));}", c1.r, c1.g, c1.b, c2.r, c2.g, c2.b); 7 | return modB; 8 | } 9 | 10 | // Internal State Structure. 11 | struct EditorCB { 12 | GtkWidget *hue; 13 | GtkWidget *saturation; 14 | GtkWidget *value; 15 | ColorHSV c; 16 | struct CallbackData *cb; 17 | }; 18 | 19 | 20 | 21 | static void update_hue(GtkWidget *self, struct EditorCB *ui) { 22 | ui->c.hue = gtk_range_get_value(GTK_RANGE(self)); 23 | ui->cb->color_data = color_hsv_re(ui->c); 24 | ColorHSV c = ui->c; 25 | 26 | create_color_range_gradient((ColorHSV){c.hue,0,c.value}, (ColorHSV){c.hue,100,c.value}); 27 | apply_style(ui->saturation, modB); 28 | 29 | create_color_range_gradient((ColorHSV){c.hue,c.saturation,0}, (ColorHSV){c.hue,c.saturation,100}); 30 | apply_style(ui->value, modB); 31 | 32 | show_color(NULL, ui->cb); 33 | } 34 | 35 | static void update_saturation(GtkWidget*self, struct EditorCB *ui) { 36 | ui->c.saturation = gtk_range_get_value(GTK_RANGE(self)); 37 | ui->cb->color_data = color_hsv_re(ui->c); 38 | ColorHSV c = ui->c; 39 | 40 | create_color_range_gradient((ColorHSV){c.hue,c.saturation,0}, (ColorHSV){c.hue,c.saturation,100}); 41 | apply_style(ui->value, modB); 42 | 43 | show_color(NULL, ui->cb); 44 | } 45 | 46 | static void update_value(GtkWidget*self, struct EditorCB *ui) { 47 | ui->c.value = gtk_range_get_value(GTK_RANGE(self)); 48 | ui->cb->color_data = color_hsv_re(ui->c); 49 | ColorHSV c = ui->c; 50 | 51 | create_color_range_gradient((ColorHSV){c.hue,0,c.value}, (ColorHSV){c.hue,100,c.value}); 52 | apply_style(ui->saturation, modB); 53 | 54 | show_color(NULL, ui->cb); 55 | } 56 | 57 | static void update_from_src(UNUSED GtkWidget* parent, GtkWidget* self, UNUSED int index, struct EditorCB *ui) { 58 | GtkWidget *area = gtk_widget_get_parent(gtk_widget_get_parent(ui->hue)); 59 | 60 | if (area == self) { 61 | ColorHSV c = color_hsv(ui->cb->color_data); 62 | gtk_range_set_value(GTK_RANGE(ui->hue), c.hue); 63 | gtk_range_set_value(GTK_RANGE(ui->saturation), c.saturation); 64 | gtk_range_set_value(GTK_RANGE(ui->value), c.value); 65 | } 66 | 67 | } 68 | 69 | void hsv_editor(GtkWidget* notebook, STATE *cbd) { 70 | struct EditorCB *ui = malloc(sizeof(struct EditorCB)); 71 | ui->cb = cbd; 72 | cbd->color_data = color_get_bg(cbd->color); // Get starting color 73 | ColorHSV c = color_hsv(cbd->color_data); 74 | ui->c = c; 75 | 76 | GtkWidget* content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); 77 | gtk_box_set_homogeneous(GTK_BOX(content), FALSE); 78 | 79 | const char rainbow[] = "trough{background-image:linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%)}"; 80 | 81 | ui->hue = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 360, 5); 82 | gtk_range_set_value(GTK_RANGE(ui->hue), c.hue); 83 | apply_style(ui->hue, (char*)rainbow); 84 | 85 | ui->saturation = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 100, 5); 86 | gtk_range_set_value(GTK_RANGE(ui->saturation), c.saturation); 87 | ui->value = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 100, 5); 88 | gtk_range_set_value(GTK_RANGE(ui->value), c.value); 89 | 90 | update_hue(ui->hue, ui); update_saturation(ui->saturation, ui);/* update_b(ui->value, ui); */ 91 | g_signal_connect(G_OBJECT(ui->hue), "value-changed", G_CALLBACK(update_hue), ui); 92 | g_signal_connect(G_OBJECT(ui->saturation), "value-changed", G_CALLBACK(update_saturation), ui); 93 | g_signal_connect(G_OBJECT(ui->value), "value-changed", G_CALLBACK(update_value), ui); 94 | 95 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->hue, "HUE")); 96 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->saturation, "SATURATION")); 97 | gtk_container_add(GTK_CONTAINER(content), label_scale(ui->value, "VALUE")); 98 | 99 | // final 100 | g_signal_connect(G_OBJECT(notebook), "switch-page", G_CALLBACK(update_from_src), ui); 101 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content, gtk_label_new("HSV")); 102 | } 103 | -------------------------------------------------------------------------------- /src/color/color.c: -------------------------------------------------------------------------------- 1 | #ifndef SCREENSHOT_PROGRAM 2 | #include 3 | #include 4 | #endif 5 | #include 6 | #include 7 | #include 8 | #include "color.h" 9 | 10 | #include 11 | 12 | /* -- CONVERSION --*/ 13 | 14 | struct Color color_from_hex(char *hex) { 15 | struct Color re; 16 | sscanf(hex, "#%02x%02x%02x", &re.r, &re.g, &re.b); 17 | return re; 18 | } 19 | 20 | void color_to_hex(struct Color color, char*space) { 21 | sprintf(space, "#%02x%02x%02x", color.r, color.g, color.b); 22 | } 23 | 24 | void color_to_rgb(struct Color color,char*space) { 25 | sprintf(space, "rgb(%d,%d,%d)", color.r, color.g, color.b); 26 | } 27 | 28 | void color_to_hsv(struct Color color, char*space) { 29 | double h, s, v; 30 | gtk_rgb_to_hsv(color.r/255., color.g/255., color.b/255., &h, &s, &v); 31 | sprintf(space, "hsv(%.0f,%.0f%%,%.0f%%)", h * 360, s * 100, v * 100); 32 | } 33 | 34 | ColorHSV color_hsv(struct Color color) { 35 | double h, s, v; 36 | gtk_rgb_to_hsv(color.r/255., color.g/255., color.b/255., &h, &s, &v); 37 | ColorHSV clr = { 38 | h * 360, s * 100, v * 100 39 | }; 40 | return clr; 41 | } 42 | 43 | Color color_hsv_re(ColorHSV T) { 44 | double r, g, b; 45 | gtk_hsv_to_rgb(T.hue/360., T.saturation/100., T.value/100., &r, &g, &b); 46 | Color c = { 47 | .r = r * 255, 48 | .g = g * 255, 49 | .b = b * 255 50 | }; 51 | return c; 52 | } 53 | 54 | /* -- GTK --*/ 55 | 56 | // Apply a color to a widget's background 57 | void color_set_bg(struct Color *color_data, GtkWidget *widget) { 58 | /* color_to_hex(color_data, space); */ 59 | /* GdkRGBA color; */ 60 | /* gdk_rgba_parse(&color, space); // TODO: Dont use this :( */ 61 | GdkRGBA color = { 62 | .red = color_data->r / 255., 63 | .blue = color_data->b / 255., 64 | .green = color_data->g / 255., 65 | .alpha = 1 66 | }; 67 | gtk_widget_override_background_color(widget, GTK_STATE_FLAG_NORMAL, &color); 68 | } 69 | 70 | struct Color color_get_bg(GtkWidget *self) { 71 | GdkRGBA color; 72 | gtk_style_context_get_background_color(gtk_widget_get_style_context(self), GTK_STATE_FLAG_NORMAL, &color); 73 | /* g_print("rgb(%f,%f,%f)", color.red, color.green, color.blue); */ 74 | struct Color color_data = { 75 | .r = color.red * 255, 76 | .b = color.blue * 255, 77 | .g = color.green * 255 78 | }; 79 | return color_data; 80 | } 81 | 82 | // Conform color to rgb limits 83 | #define color_conform(c) c > 255 ? 255 : ((c < 0) ? 0 : c) 84 | 85 | // Apply modification to all colors 86 | struct Color color_apply(struct Color *c, int amount) { 87 | struct Color out = { 88 | .r = color_conform(c->r + amount), 89 | .g = color_conform(c->g + amount), 90 | .b = color_conform(c->b + amount) 91 | }; 92 | return out; 93 | } 94 | 95 | 96 | 97 | void color_get_dominant(const char*path, struct Color *c) { 98 | // Get a image and scale it down to 1x1 to get the dominant color 99 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, 1, 1, FALSE, NULL); 100 | unsigned char *pixels = gdk_pixbuf_get_pixels(pixbuf); 101 | c->r = (uint8_t)pixels[0]; 102 | c->g = (uint8_t)pixels[1]; 103 | c->b = (uint8_t)pixels[2]; 104 | } 105 | 106 | #ifndef SCREENSHOT_PROGRAM 107 | int color_pick (struct Color *color) { 108 | int status = 0; 109 | Display *display = XOpenDisplay(NULL); 110 | Window root = DefaultRootWindow(display); 111 | 112 | Cursor cursor = XCreateFontCursor(display, 130); 113 | XGrabPointer(display, root, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime); 114 | 115 | XWindowAttributes gwa; 116 | XGetWindowAttributes(display, root, &gwa); 117 | 118 | XGrabKeyboard(display, root, 0, GrabModeAsync, GrabModeAsync, CurrentTime); 119 | 120 | for(;;) { 121 | XEvent e; 122 | XNextEvent(display, &e); 123 | if (e.type == ButtonPress && e.xbutton.button == Button1) { 124 | XImage *image = XGetImage(display, root, 0, 0, gwa.width, gwa.height, AllPlanes, ZPixmap); 125 | unsigned long pixel = XGetPixel(image, e.xbutton.x_root, e.xbutton.y_root); 126 | // printf("%d,%d,%d ", (pixel >> 0x10) & 0xFF, (pixel >> 0x08) & 0xFF, pixel & 0xFF); 127 | color->r = (pixel >> 0x10) & 0xFF; 128 | color->g = (pixel >> 0x08) & 0xFF; 129 | color->b = pixel & 0xFF; 130 | XDestroyImage(image); 131 | break; 132 | } else if (e.type == ButtonPress || 133 | (e.type == KeyPress && (e.xkey.keycode == 53))) { 134 | status = -1; 135 | break; 136 | } 137 | } 138 | 139 | /* will be done on connection close anw */ 140 | XUngrabPointer(display, CurrentTime); 141 | XUngrabKeyboard(display, CurrentTime); 142 | 143 | XFreeCursor(display, cursor); 144 | XDestroyWindow(display, root); 145 | XCloseDisplay(display); 146 | 147 | return status; 148 | } 149 | #else 150 | int color_pick (struct Color *color) { 151 | int status = 0; 152 | FILE *process = popen(SCREENSHOT_PROGRAM, "r"); 153 | if (process == NULL) { 154 | fprintf(stderr, "Failed to run command: %s - Are you using any shell script?", SCREENSHOT_PROGRAM); 155 | exit(1); 156 | } 157 | 158 | if (fscanf(process, "#%02x%02x%02x", &color->r, &color->g, &color->b) != 3) { 159 | status = -1; 160 | } 161 | 162 | pclose(process); 163 | 164 | return status; 165 | } 166 | #endif 167 | -------------------------------------------------------------------------------- /src/ui/init.c: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | #include "../util.h" 3 | #include 4 | 5 | static GtkWidget *create_color_row(const char *label, GtkWidget *panel) { 6 | GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 15); 7 | gtk_style_context_add_class(gtk_widget_get_style_context(row), "color-row"); 8 | gtk_box_pack_start(GTK_BOX(row), gtk_label_new(label), 0, 0, 0); 9 | GtkWidget *show = gtk_label_new(""); 10 | gtk_label_set_selectable(GTK_LABEL(show), 1); 11 | 12 | GtkWidget *clipboard = gtk_button_new_from_icon_name("copy-button-symbolic", GTK_ICON_SIZE_BUTTON); 13 | g_signal_connect(G_OBJECT(clipboard), "clicked", G_CALLBACK(paste_label_to_clipboard), show); 14 | g_signal_connect(G_OBJECT(clipboard), "realize", G_CALLBACK(button_cursor), NULL); 15 | 16 | gtk_box_pack_start(GTK_BOX(row), show, 1, 1, 0); 17 | gtk_box_pack_start(GTK_BOX(row), clipboard, 0, 0, 0); 18 | 19 | GtkWidget *frame = gtk_frame_new(NULL); 20 | gtk_container_add(GTK_CONTAINER(frame), row); 21 | gtk_container_add(GTK_CONTAINER(panel), frame); 22 | return show; 23 | } 24 | 25 | // Create right click menu 26 | // Callback data has no color 27 | 28 | void about_dialog(UNUSED GtkWidget *self, GtkWidget *parent); 29 | 30 | static GtkWidget *create_menu(struct CallbackData *state) { 31 | GtkWidget *right_click_menu = gtk_menu_new(); 32 | 33 | GtkWidget *items[] = { 34 | gtk_menu_item_new_with_label("Save Palette"), 35 | gtk_menu_item_new_with_label("Copy to Clipboard"), 36 | gtk_menu_item_new_with_label("Remove Color"), 37 | gtk_menu_item_new_with_label("About") 38 | }; 39 | for(int i = 0; i < sizeof(items)/sizeof(GtkWidget*); i++) { 40 | gtk_container_add(GTK_CONTAINER(right_click_menu), items[i]); 41 | } 42 | 43 | g_signal_connect(G_OBJECT(items[3]), "activate", G_CALLBACK(about_dialog), state->UI->window); 44 | g_signal_connect(G_OBJECT(items[2]), "activate", G_CALLBACK(remove_current_color), state); 45 | g_signal_connect(G_OBJECT(items[1]), "activate", G_CALLBACK(save_to_disk), state->UI->sidebar); 46 | g_signal_connect(G_OBJECT(items[0]), "activate", G_CALLBACK(paste_color_to_clipboard), &(state->color_data)); // TODO: Select the one under the mouse, not the selected one 47 | gtk_widget_show_all(right_click_menu); 48 | return right_click_menu; 49 | } 50 | 51 | static void on_first_display(UNUSED GtkWidget *self, STATE *ui) { 52 | button_cursor(ui->UI->picker, NULL); 53 | button_cursor(ui->color_light, NULL); 54 | button_cursor(ui->color_dark, NULL); 55 | 56 | gtk_widget_hide(ui->UI->panel); 57 | GtkClipboard *clip = gtk_clipboard_get_default(gdk_display_get_default()); 58 | GtkTargetEntry targets[] = { 59 | { "UTF8_STRING", 0, 0 } 60 | }; 61 | gtk_clipboard_set_can_store(clip, targets, 1); 62 | } 63 | 64 | static void before_exit(UNUSED GtkWidget *self, void *_) { 65 | GtkClipboard *clip = gtk_clipboard_get_default(gdk_display_get_default()); 66 | gtk_clipboard_store(clip); 67 | gtk_main_quit(); 68 | } 69 | 70 | STATE init_ui() { 71 | /* 72 | Window 73 | /-----------------------------------\ 74 | | Sidebar | Panel | 75 | | .----. | .--------------------. | 76 | | | 🎨 | | | Dark | XXX | Light | | 77 | | '----' | '--------------------' | 78 | | .----. | .-------------------. | 79 | | | XX | | | RGB -------- 📋 | | 80 | | '----' | '-------------------' | 81 | \-----------------------------------/ 82 | 83 | */ 84 | 85 | static UI ui; // pretty much used throughout 86 | static STATE state; 87 | 88 | ui.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 89 | gtk_window_set_title(GTK_WINDOW(ui.window), "Colr Pickr"); 90 | gtk_window_set_default_size(GTK_WINDOW(ui.window), 400, 300); 91 | gtk_window_set_type_hint(GTK_WINDOW(ui.window), GDK_WINDOW_TYPE_HINT_DIALOG); 92 | 93 | /* The weird indentation is to show the structure of the widgets with respect to each other. */ 94 | GtkWidget *layout = BOX; 95 | gtk_container_add(GTK_CONTAINER(ui.window), layout); 96 | 97 | // Create new sidebar 98 | ui.sidebar = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 99 | gtk_style_context_add_class(gtk_widget_get_style_context(ui.sidebar), "sidebar"); 100 | 101 | GtkWidget *sidebar_container = gtk_scrolled_window_new(NULL, NULL); 102 | gtk_widget_add_events(sidebar_container, GDK_BUTTON_PRESS_MASK); 103 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sidebar_container), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 104 | gtk_container_add(GTK_CONTAINER(sidebar_container), ui.sidebar); 105 | gtk_box_pack_start(GTK_BOX(layout), sidebar_container, 0, 0 , 0); 106 | 107 | // Color picker button 108 | ui.picker = gtk_button_new_from_icon_name("color-picker-button-symbolic", GTK_ICON_SIZE_BUTTON); 109 | gtk_container_add(GTK_CONTAINER(ui.sidebar), ui.picker); 110 | 111 | // Main preview panel 112 | ui.panel = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 113 | gtk_style_context_add_class(gtk_widget_get_style_context(ui.panel), "color-preview"); 114 | gtk_box_pack_end(GTK_BOX(layout), ui.panel, TRUE, TRUE, 0); 115 | 116 | // Create color display row 117 | GtkWidget *color_row = BOX; 118 | gtk_box_set_homogeneous(GTK_BOX(color_row), TRUE); 119 | gtk_container_add(GTK_CONTAINER(ui.panel), color_row); 120 | gtk_style_context_add_class(gtk_widget_get_style_context(color_row), "color-display"); 121 | 122 | state.color_light = gtk_button_new(); 123 | gtk_container_add(GTK_CONTAINER(color_row), state.color_light); 124 | 125 | state.color = gtk_button_new(); 126 | gtk_container_add(GTK_CONTAINER(color_row), state.color); 127 | 128 | state.color_dark = gtk_button_new(); 129 | gtk_container_add(GTK_CONTAINER(color_row), state.color_dark); 130 | 131 | ui.rgb_display = create_color_row("RGB", ui.panel); 132 | ui.hex_display = create_color_row("HEX", ui.panel); 133 | ui.hsv_display = create_color_row("HSV", ui.panel); 134 | 135 | state.UI = &ui; 136 | 137 | // Connect signals 138 | g_signal_connect(G_OBJECT(sidebar_container), "button-press-event", G_CALLBACK(attach_menu), create_menu(&state)); 139 | 140 | g_signal_connect(G_OBJECT(ui.window), "destroy", G_CALLBACK(before_exit), NULL); 141 | g_signal_connect(G_OBJECT(ui.window), "show", G_CALLBACK(on_first_display), &state); 142 | 143 | // Setup drag & drop 144 | GtkTargetEntry targets[] = { 145 | { "text/plain", 0, FT_PLAINTEXT }, 146 | { "text/uri-list", 0, FT_URILIST } 147 | }; 148 | gtk_drag_dest_set(ui.sidebar, GTK_DEST_DEFAULT_ALL, targets, G_N_ELEMENTS(targets), GDK_ACTION_COPY); 149 | g_signal_connect(G_OBJECT(ui.sidebar), "drag-data-received", G_CALLBACK(on_file_drop), &ui); 150 | 151 | return state; 152 | } 153 | --------------------------------------------------------------------------------