diff --git a/wpgtk/gui/color_grid.py b/wpgtk/gui/color_grid.py index 45b31c1..4e94ee4 100644 --- a/wpgtk/gui/color_grid.py +++ b/wpgtk/gui/color_grid.py @@ -10,14 +10,13 @@ from ..data import themer from . import util as gui_util -from .color_picker import ColorDialog from gi import require_version -require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GdkPixbuf # noqa: E402 +require_version("Gtk", "4.0") +from gi.repository import Gtk, Gdk, GLib # noqa: E402 +from gi.repository.GdkPixbuf import Pixbuf # noqa: E402 # TODO: remove current_walls call, use simple list -# TODO: use simple text combo # TODO: only update pixbuf if parent has same color scheme current_walls = files.get_file_list() PAD = 10 @@ -27,36 +26,36 @@ class ColorGrid(Gtk.Grid): def __init__(self, parent): Gtk.Grid.__init__(self) self.parent = parent - self.set_border_width(PAD) self.set_column_homogeneous(1) + gui_util.set_uniform_margins(self, PAD) self.set_row_spacing(PAD) self.set_column_spacing(PAD) self.colorgrid = Gtk.Grid() - self.colorgrid.set_border_width(PAD) self.colorgrid.set_column_homogeneous(1) self.colorgrid.set_row_spacing(PAD) self.colorgrid.set_column_spacing(PAD) + self.colorgrid.set_vexpand(False) - self.sat_add = Gtk.Button("+") + self.sat_add = Gtk.Button.new_with_label("+") self.sat_add.set_sensitive(False) - self.sat_red = Gtk.Button("-") + self.sat_red = Gtk.Button.new_with_label("-") self.sat_red.set_sensitive(False) - self.sat_add.connect("pressed", self.hls_change, "sat", "add") - self.sat_red.connect("pressed", self.hls_change, "sat", "red") - self.sat_lbl = Gtk.Label("Saturation:") + self.sat_add.connect("clicked", self._hls_change, "sat", "add") + self.sat_red.connect("clicked", self._hls_change, "sat", "red") + self.sat_lbl = Gtk.Label(label="Saturation:") - self.light_add = Gtk.Button("+") + self.light_add = Gtk.Button(label="+") self.light_add.set_sensitive(False) - self.light_red = Gtk.Button("-") + self.light_red = Gtk.Button(label="-") self.light_red.set_sensitive(False) - self.light_add.connect("pressed", self.hls_change, "light", "add") - self.light_red.connect("pressed", self.hls_change, "light", "red") - self.light_lbl = Gtk.Label("Brightness:") + self.light_add.connect("clicked", self._hls_change, "light", "add") + self.light_red.connect("clicked", self._hls_change, "light", "red") + self.light_lbl = Gtk.Label(label="Brightness:") self.sat_light_grid = Gtk.Grid() self.sat_light_grid.set_column_homogeneous(1) @@ -74,59 +73,56 @@ def __init__(self, parent): self.combo_grid.set_row_spacing(PAD) self.color_list = ["000000"] * 16 - self.button_list = [Gtk.Button("000000") for x in range(16)] + self.button_list = [Gtk.Button(label="000000") for x in range(16)] self.selected_file = "" for button in self.button_list: - button.connect("pressed", self.on_color_click) + button.connect("clicked", self._on_color_click) button.set_sensitive(False) cont = 0 for y in range(0, 8, 2): for x in range(0, 4): - label = Gtk.Label(cont) + label = Gtk.Label(label=cont) self.colorgrid.attach(label, x, y, 1, 1) self.colorgrid.attach(self.button_list[cont], x, y + 1, 1, 1) cont += 1 sample_name = os.path.join(SAMPLE_DIR, ".no_sample.sample.png") - self.sample = Gtk.Image() - - pixbuf_sample = gui_util.get_sample_pixbuf(sample_name) - if pixbuf_sample is not None: - self.sample.set_from_pixbuf(self.pixbuf_sample) - - self.shuffle_button = Gtk.Button("Shuffle colors") - self.shuffle_button.connect("pressed", self.on_shuffle_click) + self._sample_pixbuf = None + self.sample = Gtk.DrawingArea() + self.sample.set_content_height(50) + self.sample.set_hexpand(True) + self.sample.set_vexpand(False) + self.sample.set_valign(Gtk.Align.START) + self.sample.set_draw_func(gui_util.draw_sample, lambda: self._sample_pixbuf) + self._set_sample_file(sample_name) + + self.shuffle_button = Gtk.Button(label="Shuffle colors") + self.shuffle_button.connect("clicked", self._on_shuffle_click) self.shuffle_button.set_sensitive(False) - self.import_button = Gtk.Button("import") + self.import_button = Gtk.Button(label="Import") self.import_button.set_sensitive(False) - self.import_button.connect("pressed", self.on_import_click) + self.import_button.connect("clicked", self._on_import_click) - self.ok_button = Gtk.Button("Save") - self.ok_button.connect("pressed", self.on_ok_click) + self.ok_button = Gtk.Button(label="Save") + self.ok_button.connect("clicked", self._on_ok_click) self.ok_button.set_sensitive(False) - self.auto_button = Gtk.Button("Auto-adjust") - self.auto_button.connect("pressed", self.on_auto_click) + self.auto_button = Gtk.Button(label="Auto-adjust") + self.auto_button.connect("clicked", self._on_auto_click) self.auto_button.set_sensitive(False) - self.reset_button = Gtk.Button("Reset") + self.reset_button = Gtk.Button(label="Reset") self.reset_button.set_sensitive(False) - self.reset_button.connect("pressed", self.on_reset_click) - - self.done_lbl = Gtk.Label("") + self.reset_button.connect("clicked", self._on_reset_click) - option_list = Gtk.ListStore(str) - for elem in list(files.get_file_list()): - option_list.append([elem]) + self.done_lbl = Gtk.Label(label="") - self.option_combo = Gtk.ComboBox.new_with_model(option_list) - self.renderer_text = Gtk.CellRendererText() - self.option_combo.pack_start(self.renderer_text, True) - self.option_combo.add_attribute(self.renderer_text, "text", 0) - self.option_combo.set_entry_text_column(0) - self.option_combo.connect("changed", self.combo_box_change) + self.option_combo = Gtk.ComboBoxText() + for elem in files.get_file_list(): + self.option_combo.append_text(elem) + self.option_combo.connect("changed", self._combo_box_change) self.combo_grid.attach(self.option_combo, 0, 0, 3, 1) self.combo_grid.attach(self.reset_button, 3, 0, 1, 1) @@ -151,17 +147,24 @@ def __init__(self, parent): self.attach(self.sat_light_grid, 0, 4, 1, 1) self.attach(self.done_lbl, 0, 5, 1, 1) + def _set_sample_file(self, path): + try: + self._sample_pixbuf = Pixbuf.new_from_file(path) + except Exception: + self._sample_pixbuf = None + self.sample.queue_draw() + def render_buttons(self): for x, button in enumerate(self.button_list): - gcolor = Gdk.color_parse(self.color_list[x]) if util.get_hls_val(self.color_list[x], "light") < 99: - fgcolor = Gdk.color_parse("#FFFFFF") + fgcolor = "#FFFFFF" else: - fgcolor = Gdk.color_parse("#000000") + fgcolor = "#000000" button.set_label(self.color_list[x]) button.set_sensitive(True) - button.modify_bg(Gtk.StateType.NORMAL, gcolor) - button.modify_fg(Gtk.StateType.NORMAL, fgcolor) + gui_util.set_widget_colors( + button, background=self.color_list[x], foreground=fgcolor + ) def render_theme(self): sample_path = files.get_sample_path(self.selected_file) @@ -172,15 +175,13 @@ def render_theme(self): self.color_list = themer.set_fallback_theme(self.selected_file) self.render_buttons() - pixbuf_sample = gui_util.get_sample_pixbuf(sample_path) - if pixbuf_sample is None: + if not os.path.isfile(sample_path): sample.create_sample(self.color_list, sample_path) - pixbuf_sample = gui_util.get_sample_pixbuf(sample_path) - self.sample.set_from_pixbuf(pixbuf_sample) - self.parent.sample.set_from_pixbuf(pixbuf_sample) + self._set_sample_file(sample_path) + self.parent._set_sample_file(sample_path) - def hls_change(self, widget, *gparam): + def _hls_change(self, widget, *gparam): if gparam[0] == "sat": val = 0.05 if gparam[1] == "add" else -0.05 self.color_list = [ @@ -197,12 +198,9 @@ def hls_change(self, widget, *gparam): def render_sample(self): sample.create_sample(self.color_list) sample_path = os.path.join(SAMPLE_DIR, ".tmp.sample.png") - self.pixbuf_sample = GdkPixbuf.Pixbuf.new_from_file_at_size( - sample_path, width=500, height=300 - ) - self.sample.set_from_pixbuf(self.pixbuf_sample) + self._set_sample_file(sample_path) - def on_ok_click(self, widget): + def _on_ok_click(self, widget): color.write_colors(self.selected_file, self.color_list) tmpfile = os.path.join(SAMPLE_DIR, ".tmp.sample.png") @@ -214,80 +212,83 @@ def on_ok_click(self, widget): self.done_lbl.set_text("Changes saved") sample_path = files.get_sample_path(self.selected_file) - self.parent.pixbuf_sample = GdkPixbuf.Pixbuf.new_from_file_at_size( - sample_path, width=500, height=300 - ) - self.parent.sample.set_from_pixbuf(self.pixbuf_sample) + self.parent._set_sample_file(sample_path) - def on_auto_click(self, widget): + def _on_auto_click(self, widget): self.color_list = color.auto_adjust(self.color_list) self.render_buttons() self.render_sample() - def on_reset_click(self, widget): + def _on_reset_click(self, widget): themer.reset_theme(self.selected_file) self.render_theme() - def on_import_click(self, widget): - fcd = Gtk.FileChooserDialog( - "Select a colorscheme", - self.parent, - Gtk.FileChooserAction.OPEN, - ( - Gtk.STOCK_CANCEL, - Gtk.ResponseType.CANCEL, - Gtk.STOCK_OPEN, - Gtk.ResponseType.OK, - ), - ) + def _on_import_click(self, widget): + fcd = Gtk.FileDialog() filter = Gtk.FileFilter() filter.set_name("JSON colorscheme") filter.add_mime_type("application/json") - fcd.add_filter(filter) - response = fcd.run() - if response == Gtk.ResponseType.OK: - self.color_list = color.get_color_list(fcd.get_filename(), True) - self.render_buttons() - self.render_sample() - fcd.destroy() + fcd.set_default_filter(filter) + fcd.set_title("Select a colorscheme") + + fcd.open(parent=self.parent, callback=self._on_import_finish) + + def _on_import_finish(self, dialog, result): + try: + filename = dialog.open_finish(result) - def on_shuffle_click(self, widget): + if filename: + self.color_list = color.get_color_list(filename, True) + self.render_buttons() + self.render_sample() + except GLib.Error as error: + print(f"Error opening file: {error.message}") + + def _on_shuffle_click(self, widget): self.color_list = color.shuffle_colors(self.color_list) self.render_buttons() self.render_sample() - def on_color_click(self, widget): + def _on_color_click(self, widget): self.done_lbl.set_text("") + self.active_color_button = widget gcolor = Gdk.RGBA() gcolor.parse(widget.get_label()) - dialog = ColorDialog(self.parent, self.selected_file, gcolor) - response = dialog.run() + dialog = Gtk.ColorDialog() + dialog.set_with_alpha(False) + dialog.set_title("Choose a Color") + dialog.choose_rgba( + parent=self.parent, initial_color=gcolor, callback=self._on_color_selected + ) - if response == Gtk.ResponseType.OK: - r, g, b, _ = dialog.colorchooser.get_rgba() + def _on_color_selected(self, dialog, result): + rgba = dialog.choose_rgba_finish(result) + + if rgba: + r, g, b, _ = rgba rgb = list(map(lambda x: round(x * 100 * 2.55), [r, g, b])) hex_color = pywal.util.rgb_to_hex(rgb) - widget.set_label(hex_color) + # widget.set_label(hex_color) - gcolor = Gdk.color_parse(hex_color) if util.get_hls_val(hex_color, "light") < 100: - fgcolor = Gdk.color_parse("#FFFFFF") + fgcolor = "#FFFFFF" else: - fgcolor = Gdk.color_parse("#000000") + fgcolor = "#000000" - widget.set_sensitive(True) - widget.modify_bg(Gtk.StateType.NORMAL, gcolor) - widget.modify_fg(Gtk.StateType.NORMAL, fgcolor) + self.active_color_button.set_sensitive(True) + self.active_color_button.set_label(hex_color) + gui_util.set_widget_colors( + self.active_color_button, background=hex_color, foreground=fgcolor + ) for i, c in enumerate(self.button_list): if c.get_label() != self.color_list[i]: self.color_list[i] = c.get_label() self.render_sample() - dialog.destroy() - def combo_box_change(self, widget): + def _combo_box_change(self, widget): self.done_lbl.set_text("") x = self.option_combo.get_active() diff --git a/wpgtk/gui/color_picker.py b/wpgtk/gui/color_picker.py deleted file mode 100644 index f8a5f79..0000000 --- a/wpgtk/gui/color_picker.py +++ /dev/null @@ -1,70 +0,0 @@ -from ..data import util -from gi import require_version -require_version("Gtk", "3.0") -require_version("Gdk", "3.0") -from gi.repository import Gtk # noqa: E402 -from gi.repository import Gdk # noqa: E402 - - -class ColorDialog(Gtk.Dialog): - - def __init__(self, parent, current_file, gcolor): - Gtk.Dialog.__init__(self, "Pick a Color", parent, 0, - (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_OK, Gtk.ResponseType.OK)) - - self.set_default_size(150, 100) - box = self.get_content_area() - box.set_border_width(10) - box.set_spacing(10) - - sat_box = Gtk.Box(spacing=10, orientation=Gtk.Orientation.HORIZONTAL) - light_box = Gtk.Box(spacing=10, orientation=Gtk.Orientation.HORIZONTAL) - - self.colorchooser = Gtk.ColorChooserWidget(show_editor=True) - self.colorchooser.set_use_alpha(False) - self.colorchooser.set_rgba(gcolor) - - r, g, b, _ = list(map(lambda x: round(x*100*2.55), gcolor)) - hue, light, sat = util.rgb_to_hls(r, g, b) - - self.sat_lbl = Gtk.Label('Saturation') - self.light_lbl = Gtk.Label('Light ') - - sat_range = Gtk.Adjustment(0, 0, 1, 0.1, 0.1, 0) - self.sat_slider = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, - adjustment=sat_range) - self.sat_slider.set_value(-sat) - self.sat_slider.set_digits(2) - self.sat_slider.connect('value-changed', self.slider_changed, 'sat') - - light_range = Gtk.Adjustment(5, 0, 255, 1, 10, 0) - self.light_slider = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, - adjustment=light_range) - self.light_slider.set_value(light) - self.light_slider.connect('value-changed', - self.slider_changed, 'light') - - box.add(self.colorchooser) - - sat_box.pack_start(self.sat_lbl, True, True, 0) - sat_box.pack_start(self.sat_slider, True, True, 0) - - light_box.pack_start(self.light_lbl, True, True, 0) - light_box.pack_start(self.light_slider, True, True, 0) - - box.add(light_box) - box.add(sat_box) - - self.show_all() - - def slider_changed(self, slider, *arg): - newval = -slider.get_value() if arg[0] == 'sat' else slider.get_value() - - red, green, blue, _ = self.colorchooser.get_rgba() - rgb = list(map(lambda x: round(x*100*2.55), [red, green, blue])) - newhex = util.set_hls_val(util.rgb_to_hex(rgb), arg[0], newval) - - new_gcolor = Gdk.RGBA() - new_gcolor.parse(newhex) - self.colorchooser.set_rgba(new_gcolor) diff --git a/wpgtk/gui/keyword_dialog.py b/wpgtk/gui/keyword_dialog.py index 684bfd6..cab917b 100644 --- a/wpgtk/gui/keyword_dialog.py +++ b/wpgtk/gui/keyword_dialog.py @@ -1,29 +1,72 @@ from gi import require_version -require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa: E402 +from . import util -class KeywordDialog(Gtk.Dialog): +require_version("Gtk", "4.0") +from gi.repository import Gtk, Gdk # noqa: E402 - def __init__(self, parent): - Gtk.Dialog.__init__(self, "Name you keyword/value set", parent, 0, - (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_OK, Gtk.ResponseType.OK)) - self.set_default_size(150, 100) +class KeywordDialog(Gtk.Window): + def __init__(self, parent, callback): + super().__init__(title="Name your keyword/value set") + self.callback = callback + + self.set_transient_for(parent) + self.set_default_size(200, 100) + self.set_modal(True) + self.set_resizable(False) + self.name_text_input = Gtk.Entry() + # Handle Enter key + self.name_text_input.connect("activate", lambda e: self._on_ok_clicked(None)) self.error_lbl = Gtk.Label() - box = self.get_content_area() - box.set_border_width(10) - box.set_spacing(10) - box.add(self.name_text_input) - box.add(self.error_lbl) + # Handle exit with Esc + key_controller = Gtk.EventControllerKey() + key_controller.connect("key-pressed", self._on_key_pressed) + self.add_controller(key_controller) + + ok_button = Gtk.Button(label="OK") + cancel_button = Gtk.Button(label="Cancel") + + ok_button.connect("clicked", self._on_ok_clicked) + cancel_button.connect("clicked", self._on_cancel_clicked) + + button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + button_box.set_halign(Gtk.Align.END) + button_box.append(cancel_button) + button_box.append(ok_button) - self.show_all() + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + util.set_uniform_margins(box, 10) + box.append(self.name_text_input) + box.append(self.error_lbl) + box.append(button_box) + + self.set_child(box) def get_section_name(self): if len(self.name_text_input.get_text()) <= 0: - raise Exception('Empty name not allowed') + raise Exception("Empty name not allowed") return self.name_text_input.get_text() + + def _on_ok_clicked(self, button): + try: + name = self.get_section_name() + self.callback(Gtk.ResponseType.OK, name) + self.close() + except Exception as e: + self.error_lbl.set_text(str(e)) + self.error_lbl.set_visible(True) + + def _on_cancel_clicked(self, button): + self.callback(Gtk.ResponseType.CANCEL, None) + self.close() + + def _on_key_pressed(self, controller, keyval, keycode, state): + if keyval == Gdk.KEY_Escape: + self._on_cancel_clicked(None) + return True + + return False # Event not handled diff --git a/wpgtk/gui/keyword_grid.py b/wpgtk/gui/keyword_grid.py index 6e85ec4..6fb1ebc 100644 --- a/wpgtk/gui/keyword_grid.py +++ b/wpgtk/gui/keyword_grid.py @@ -1,8 +1,10 @@ import logging from ..data import keywords from ..data.config import user_keywords, settings, write_conf +from . import util from gi import require_version -require_version("Gtk", "3.0") + +require_version("Gtk", "4.0") from .keyword_dialog import KeywordDialog # noqa: E402 from gi.repository import Gtk # noqa: E402 @@ -16,48 +18,48 @@ def __init__(self, parent): Gtk.Grid.__init__(self) self.parent = parent - self.set_border_width(PAD) self.set_column_homogeneous(1) + util.set_uniform_margins(self, PAD) self.set_row_spacing(PAD) self.set_column_spacing(PAD) self.liststore = Gtk.ListStore(str, str) - self.remove_button = Gtk.Button('Remove Keyword') - self.remove_button.connect('clicked', self.remove_keyword) + self.remove_button = Gtk.Button(label="Remove Keyword") + self.remove_button.connect("clicked", self._remove_keyword) - self.add_button = Gtk.Button('Add Keyword') - self.add_button.connect('clicked', self.append_new_keyword) + self.add_button = Gtk.Button(label="Add Keyword") + self.add_button.connect("clicked", self._append_new_keyword) - self.choose_button = Gtk.Button('Choose Set') - self.choose_button.connect('clicked', self.choose_keywords_section) + self.choose_button = Gtk.Button(label="Choose Set") + self.choose_button.connect("clicked", self._choose_keywords_section) - self.create_button = Gtk.Button('Create Set') - self.create_button.connect('clicked', self.create_keywords_section) + self.create_button = Gtk.Button(label="Create Set") + self.create_button.connect("clicked", self._create_keywords_section) - self.delete_button = Gtk.Button('Delete Set') - self.delete_button.connect('clicked', self.delete_keywords_section) + self.delete_button = Gtk.Button(label="Delete Set") + self.delete_button.connect("clicked", self._delete_keywords_section) self.sections_combo = Gtk.ComboBoxText() - self.sections_combo.connect("changed", self.on_section_change) + self.sections_combo.connect("changed", self._on_section_change) self.reload_section_list() self.selected_file = settings.get("keywords", "default") idx = list(user_keywords.sections()).index(self.selected_file) self.sections_combo.set_active(idx) - self.delete_button.set_sensitive(self.selected_file != 'default') + self.delete_button.set_sensitive(self.selected_file != "default") self.choose_button.set_sensitive(False) self.reload_keyword_list() - self.status_lbl = Gtk.Label('') + self.status_lbl = Gtk.Label(label="") self.keyword_tree = Gtk.TreeView(model=self.liststore) scroll = Gtk.ScrolledWindow() scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scroll.set_min_content_height(320) scroll.set_propagate_natural_height(True) - scroll.add(self.keyword_tree) + scroll.set_child(self.keyword_tree) self.attach(self.sections_combo, 0, 0, 2, 1) self.attach(self.choose_button, 2, 0, 1, 1) @@ -69,12 +71,12 @@ def __init__(self, parent): self.attach(self.status_lbl, 0, 4, 4, 1) key_renderer = Gtk.CellRendererText() - key_renderer.set_property('editable', True) - key_renderer.connect('edited', self.text_edited, 0) + key_renderer.set_property("editable", True) + key_renderer.connect("edited", self._text_edited, 0) value_renderer = Gtk.CellRendererText() - value_renderer.set_property('editable', True) - value_renderer.connect('edited', self.text_edited, 1) + value_renderer.set_property("editable", True) + value_renderer.connect("edited", self._text_edited, 1) keyword_text = Gtk.TreeViewColumn("Keyword", key_renderer, text=0) self.keyword_tree.append_column(keyword_text) @@ -82,8 +84,8 @@ def __init__(self, parent): value_text = Gtk.TreeViewColumn("Value", value_renderer, text=1) self.keyword_tree.append_column(value_text) - def remove_keyword(self, widget): - self.status_lbl.set_text('') + def _remove_keyword(self, widget): + self.status_lbl.set_text("") (m, pathlist) = self.keyword_tree.get_selection().get_selected_rows() for path in pathlist: @@ -92,23 +94,21 @@ def remove_keyword(self, widget): keywords.remove_pair(value, self.selected_file) self.reload_keyword_list() - def text_edited(self, widget, path, text, col): - self.status_lbl.set_text('') - if (col == 0): + def _text_edited(self, widget, path, text, col): + self.status_lbl.set_text("") + if col == 0: try: - keywords.update_key(self.liststore[path][col], text, - self.selected_file) + keywords.update_key(self.liststore[path][col], text, self.selected_file) except Exception as e: self.status_lbl.set_text(str(e)) else: try: - keywords.update_value(self.liststore[path][0], text, - self.selected_file) + keywords.update_value(self.liststore[path][0], text, self.selected_file) except Exception as e: self.status_lbl.set_text(str(e)) self.reload_keyword_list() - def reload_section_list(self, active='default'): + def reload_section_list(self, active="default"): sections = list(user_keywords.sections()) self.sections_combo.remove_all() @@ -124,46 +124,43 @@ def reload_keyword_list(self): for k, v in keyword_section.items(): self.liststore.append([k, v]) - def on_section_change(self, widget): + def _on_section_change(self, widget): self.selected_file = widget.get_active_text() if self.selected_file is not None: self.reload_keyword_list() self.choose_button.set_sensitive( - settings.get('keywords', 'default') != self.selected_file + settings.get("keywords", "default") != self.selected_file ) - settings['keywords'] = self.selected_file - self.delete_button.set_sensitive(self.selected_file != 'default') + settings["keywords"] = self.selected_file + self.delete_button.set_sensitive(self.selected_file != "default") - def append_new_keyword(self, widget): - self.status_lbl.set_text('') + def _append_new_keyword(self, widget): + self.status_lbl.set_text("") keywords.create_pair( - 'keyword' + str(len(self.liststore)), - 'value', + "keyword" + str(len(self.liststore)), + "value", self.selected_file, ) self.reload_keyword_list() - def delete_keywords_section(self, widget): + def _delete_keywords_section(self, widget): if self.selected_file: - keywords.delete_keywords_section(self.selected_file) + keywords._delete_keywords_section(self.selected_file) self.reload_section_list() - def choose_keywords_section(self, widget): + def _choose_keywords_section(self, widget): write_conf() self.choose_button.set_sensitive(False) - def create_keywords_section(self, widget): - dialog = KeywordDialog(self.parent) - response = dialog.run() + def _create_keywords_section(self, widget): + dialog = KeywordDialog(self.parent, self._handle_new_keyword_section) + dialog.present() + def _handle_new_keyword_section(self, response, value): if response == Gtk.ResponseType.OK: try: - section = dialog.get_section_name() - keywords.create_keywords_section(section) - self.reload_section_list(section) + keywords._create_keywords_section(value) + self.reload_section_list(value) except Exception as e: logging.error(str(e)) - dialog.destroy() - if response == Gtk.ResponseType.CANCEL: - dialog.destroy() diff --git a/wpgtk/gui/option_grid.py b/wpgtk/gui/option_grid.py index 809e807..1d0e759 100644 --- a/wpgtk/gui/option_grid.py +++ b/wpgtk/gui/option_grid.py @@ -1,139 +1,113 @@ from gi import require_version +from . import util from ..data.config import settings, write_conf from pywal import colors -require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk # noqa: E402 + +require_version("Gtk", "4.0") +from gi.repository import Gtk # noqa: E402 PAD = 10 class OptionsGrid(Gtk.Grid): - def __init__(self, parent): + def __init__(self, parent): Gtk.Grid.__init__(self) self.parent = parent - self.set_border_width(PAD) self.set_column_homogeneous(1) self.set_row_spacing(PAD) self.set_column_spacing(PAD) # Switch Grid self.switch_grid = Gtk.Grid() - self.switch_grid.set_border_width(PAD) self.switch_grid.set_column_homogeneous(1) + util.set_uniform_margins(self, PAD) self.switch_grid.set_row_spacing(PAD) self.switch_grid.set_column_spacing(PAD) # Active Color Grid self.active_grid = Gtk.Grid() - self.active_grid.set_border_width(PAD) self.active_grid.set_column_homogeneous(1) self.active_grid.set_row_spacing(PAD) self.active_grid.set_column_spacing(PAD) # Setting up ComboBox - color_list = ['Random'] + [str(x) for x in range(1, 16)] + color_list = ["Random"] + [str(x) for x in range(1, 16)] self.color_combo = Gtk.ComboBoxText() for elem in list(color_list): self.color_combo.append_text(elem) - self.color_combo.connect("changed", self.combo_box_change, "active") + self.color_combo.connect("changed", self._combo_box_change, "active") # Button - self.color_button = Gtk.Button("Active/Inactive Color") - self.save_button = Gtk.Button("Save") - self.save_button.connect("pressed", self.on_save_button) + self.color_button = Gtk.Button(label="Active/Inactive Color") + self.save_button = Gtk.Button(label="Save") + self.save_button.connect("clicked", self._on_save_button) # Backend Combo - self.backend_lbl = Gtk.Label("Select your backend:") + self.backend_lbl = Gtk.Label(label="Select your backend:") self.backend_combo = Gtk.ComboBoxText() self.backend_list = colors.list_backends() for elem in self.backend_list: self.backend_combo.append_text(elem) - self.backend_combo.connect("changed", self.combo_box_change, "backend") + self.backend_combo.connect("changed", self._combo_box_change, "backend") # Switches self.gtk_switch = Gtk.Switch() - self.gtk_switch.connect("notify::active", self.on_activate, "gtk") - self.lbl_gtk = Gtk.Label("Reload GTK+") + self.gtk_switch.connect("notify::active", self._on_activate, "gtk") + self.lbl_gtk = Gtk.Label(label="Reload GTK+") self.vte_switch = Gtk.Switch() - self.vte_switch.connect( - "notify::active", - self.on_activate, - "vte" - ) - self.lbl_vte = Gtk.Label("Use VTE Fix") + self.vte_switch.connect("notify::active", self._on_activate, "vte") + self.lbl_vte = Gtk.Label(label="Use VTE Fix") self.light_theme_switch = Gtk.Switch() self.light_theme_switch.connect( - "notify::active", - self.on_activate, - "light_theme" + "notify::active", self._on_activate, "light_theme" ) - self.lbl_light_theme = Gtk.Label("Use light themes") + self.lbl_light_theme = Gtk.Label(label="Use light themes") self.wallpaper_switch = Gtk.Switch() self.wallpaper_switch.connect( - "notify::active", - self.on_activate, - "set_wallpaper" + "notify::active", self._on_activate, "set_wallpaper" ) - self.lbl_wallpaper = Gtk.Label("Set wallpaper") + self.lbl_wallpaper = Gtk.Label(label="Set wallpaper") self.smart_sort_switch = Gtk.Switch() - self.smart_sort_switch.connect( - "notify::active", - self.on_activate, - "smart_sort" - ) - self.lbl_smart_sort = Gtk.Label("Use smart sort") + self.smart_sort_switch.connect("notify::active", self._on_activate, "smart_sort") + self.lbl_smart_sort = Gtk.Label(label="Use smart sort") self.auto_adjust_switch = Gtk.Switch() self.auto_adjust_switch.connect( - "notify::active", - self.on_activate, - "auto_adjust" + "notify::active", self._on_activate, "auto_adjust" ) - self.lbl_auto_adjust = Gtk.Label("Always auto adjust") + self.lbl_auto_adjust = Gtk.Label(label="Always auto adjust") self.reload_switch = Gtk.Switch() - self.reload_switch.connect( - "notify::active", - self.on_activate, - "reload" - ) - self.lbl_reload = Gtk.Label("Reload other software") + self.reload_switch.connect("notify::active", self._on_activate, "reload") + self.lbl_reload = Gtk.Label(label="Reload other software") self.terminal_switch = Gtk.Switch() - self.terminal_switch.connect( - "notify::active", - self.on_activate, - "terminal" - ) - self.lbl_terminal = Gtk.Label("Change terminal colors") + self.terminal_switch.connect("notify::active", self._on_activate, "terminal") + self.lbl_terminal = Gtk.Label(label="Change terminal colors") # edit cmd - self.editor_lbl = Gtk.Label("Open optional files with:") + self.editor_lbl = Gtk.Label(label="Open optional files with:") self.editor_txt = Gtk.Entry() - self.editor_txt.connect("changed", self.on_txt_change, "editor") + self.editor_txt.connect("changed", self._on_txt_change, "editor") # cmd - self.command_lbl = Gtk.Label("Run command after") - self.command_exe_lbl = Gtk.Label("Command: ") + self.command_lbl = Gtk.Label(label="Run command after") + self.command_exe_lbl = Gtk.Label(label="Command: ") self.command_txt = Gtk.Entry() - self.command_txt.connect("changed", self.on_txt_change, "command") + self.command_txt.connect("changed", self._on_txt_change, "command") self.command_switch = Gtk.Switch() - self.command_switch.connect( - "notify::active", - self.on_activate, - "execute_cmd" - ) + self.command_switch.connect("notify::active", self._on_activate, "execute_cmd") - self.alpha_lbl = Gtk.Label('Alpha:') + self.alpha_lbl = Gtk.Label(label="Alpha:") self.alpha_txt = Gtk.Entry() - self.alpha_txt.connect("changed", self.on_txt_change, "alpha") + self.alpha_txt.connect("changed", self._on_txt_change, "alpha") self.load_opt_list() # Switch Grid attach @@ -181,13 +155,13 @@ def __init__(self, parent): self.active_grid.attach(self.save_button, 1, 6, 2, 1) - self.attach(self.switch_grid, 1, 1, 1, 1) - self.attach(self.active_grid, 1, 2, 1, 1) + self.attach(self.switch_grid, 1, 1, 1, 1) + self.attach(self.active_grid, 1, 2, 1, 1) self.save_button.set_sensitive(False) - def on_activate(self, switch, *gparam): - if(gparam[1] == 'execute_cmd'): + def _on_activate(self, switch, *gparam): + if gparam[1] == "execute_cmd": self.command_txt.set_editable(switch.get_active()) settings[gparam[1]] = str(switch.get_active()).lower() self.save_button.set_sensitive(True) @@ -197,52 +171,38 @@ def load_opt_list(self): idx = self.backend_list.index(current_backend) self.backend_combo.set_active(idx) - self.color_combo\ - .set_active(settings.getint("active", 0)) - self.gtk_switch\ - .set_active(settings.getboolean("gtk", True)) - self.command_switch\ - .set_active(settings.getboolean("execute_cmd", False)) - self.light_theme_switch\ - .set_active(settings.getboolean("light_theme", False)) - self.vte_switch\ - .set_active(settings.getboolean("vte", False)) - self.wallpaper_switch\ - .set_active(settings.getboolean("set_wallpaper", True)) - self.smart_sort_switch\ - .set_active(settings.getboolean("smart_sort", True)) - self.auto_adjust_switch\ - .set_active(settings.getboolean("auto_adjust", False)) - self.reload_switch\ - .set_active(settings.getboolean("reload", True)) - self.terminal_switch\ - .set_active(settings.getboolean("terminal", True)) - - self.editor_txt\ - .set_text(settings.get("editor", "urxvt -e vim")) - self.command_txt\ - .set_text(settings.get("command", "yes hi")) - self.command_txt\ - .set_editable(settings.getboolean("execute_cmd", False)) - self.alpha_txt\ - .set_text(settings.get("alpha", "100")) - - def combo_box_change(self, combo, *gparam): + self.color_combo.set_active(settings.getint("active", 0)) + self.gtk_switch.set_active(settings.getboolean("gtk", True)) + self.command_switch.set_active(settings.getboolean("execute_cmd", False)) + self.light_theme_switch.set_active(settings.getboolean("light_theme", False)) + self.vte_switch.set_active(settings.getboolean("vte", False)) + self.wallpaper_switch.set_active(settings.getboolean("set_wallpaper", True)) + self.smart_sort_switch.set_active(settings.getboolean("smart_sort", True)) + self.auto_adjust_switch.set_active(settings.getboolean("auto_adjust", False)) + self.reload_switch.set_active(settings.getboolean("reload", True)) + self.terminal_switch.set_active(settings.getboolean("terminal", True)) + + self.editor_txt.set_text(settings.get("editor", "urxvt -e vim")) + self.command_txt.set_text(settings.get("command", "yes hi")) + self.command_txt.set_editable(settings.getboolean("execute_cmd", False)) + self.alpha_txt.set_text(settings.get("alpha", "100")) + + def _combo_box_change(self, combo, *gparam): x = combo.get_active() item = combo.get_active_text() if gparam[0] == "active": settings[gparam[0]] = str(x) - color = Gdk.color_parse(self.parent.cpage.color_list[x]) - self.color_button.modify_bg(Gtk.StateType.NORMAL, color) + bgcolor = f"#{self.parent.cpage.color_list[x]}" + util.set_widget_colors(self.color_button, background=bgcolor) if gparam[0] == "backend": settings[gparam[0]] = item self.save_button.set_sensitive(True) - def on_txt_change(self, gtk_entry, *gparam): + def _on_txt_change(self, gtk_entry, *gparam): settings[gparam[0]] = gtk_entry.get_text() self.save_button.set_sensitive(True) - def on_save_button(self, button): + def _on_save_button(self, button): write_conf() self.save_button.set_sensitive(False) diff --git a/wpgtk/gui/template_grid.py b/wpgtk/gui/template_grid.py index c7b9c16..7296bbb 100644 --- a/wpgtk/gui/template_grid.py +++ b/wpgtk/gui/template_grid.py @@ -4,29 +4,30 @@ from subprocess import Popen from ..data.config import OPT_DIR, settings from ..data import files +from . import util from gi import require_version -require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa: E402 + +require_version("Gtk", "4.0") +from gi.repository import Gtk, Gdk, GLib # noqa: E402 from gi.repository.GdkPixbuf import Pixbuf # noqa: E402 PAD = 10 -icon = 'document-open' +icon = "document-open" class TemplateGrid(Gtk.Grid): - """A helper for choosing config files that will be modified with wpgtk's help""" def __init__(self, parent): Gtk.Grid.__init__(self) self.current = None - self.sel_file = '' + self.sel_file = "" self.parent = parent - self.set_border_width(PAD) self.set_column_homogeneous(1) + util.set_uniform_margins(self, PAD) self.set_row_spacing(PAD) self.set_column_spacing(PAD) @@ -35,12 +36,12 @@ def __init__(self, parent): self.grid_edit.set_row_spacing(PAD) self.grid_edit.set_column_spacing(PAD) - self.button_add = Gtk.Button('Add') - self.button_add.connect('clicked', self.on_add_clicked) - self.button_rm = Gtk.Button('Remove') - self.button_rm.connect('clicked', self.on_rm_clicked) - self.button_edit = Gtk.Button('Edit') - self.button_edit.connect('clicked', self.on_open_clicked) + self.button_add = Gtk.Button(label="Add") + self.button_add.connect("clicked", self._on_add_clicked) + self.button_rm = Gtk.Button(label="Remove") + self.button_rm.connect("clicked", self._on_rm_clicked) + self.button_edit = Gtk.Button(label="Edit") + self.button_edit.connect("clicked", self._on_open_clicked) self.liststore = Gtk.ListStore(Pixbuf, str) self.file_view = Gtk.IconView.new() @@ -48,18 +49,20 @@ def __init__(self, parent): self.file_view.set_activate_on_single_click(True) self.file_view.set_pixbuf_column(0) self.file_view.set_text_column(1) - self.file_view.connect('item-activated', self.on_file_click) + self.file_view.set_item_width(96) + self.file_view.connect("item-activated", self._on_file_click) self.scroll = Gtk.ScrolledWindow() self.scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.scroll.set_min_content_height(400) - self.scroll.add(self.file_view) + self.scroll.set_child(self.file_view) self.item_names = files.get_file_list(OPT_DIR, r".*\.base$") + self.icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()) + self._icon_pixbuf = self._load_icon_pixbuf() for filen in self.item_names: - pixbuf = Gtk.IconTheme.get_default().load_icon(icon, 64, 0) - self.liststore.append([pixbuf, filen]) + self.liststore.append([self._icon_pixbuf, filen]) self.grid_edit.attach(self.button_add, 0, 0, 2, 1) self.grid_edit.attach(self.button_edit, 0, 1, 1, 1) @@ -68,56 +71,62 @@ def __init__(self, parent): self.attach(self.grid_edit, 0, 0, 1, 1) - def on_add_clicked(self, widget): - filechooser = Gtk.FileChooserDialog("Select an Image", self.parent, - Gtk.FileChooserAction.OPEN, - (Gtk.STOCK_CANCEL, - Gtk.ResponseType.CANCEL, - Gtk.STOCK_OPEN, - Gtk.ResponseType.OK)) + def _load_icon_pixbuf(self): + paintable = self.icon_theme.lookup_icon( + icon, None, 64, 1, Gtk.TextDirection.LTR, Gtk.IconLookupFlags.FORCE_REGULAR + ) + icon_file = paintable.get_file() + if icon_file: + return Pixbuf.new_from_file_at_size(icon_file.get_path(), 64, 64) + return None + + def _refresh_liststore(self): + self.item_names = files.get_file_list(OPT_DIR, r".*\.base$") + self.liststore.clear() + for filen in self.item_names: + self.liststore.append([self._icon_pixbuf, filen]) + self.file_view.unselect_all() + + def _on_add_clicked(self, widget): + filechooser = Gtk.FileDialog() + filechooser.set_title("Select a file") + filefilter = Gtk.FileFilter() - filechooser.set_select_multiple(True) filefilter.set_name("Text") filefilter.add_mime_type("text/*") - filechooser.add_filter(filefilter) - response = filechooser.run() - - if response == Gtk.ResponseType.OK: - for f in filechooser.get_filenames(): - files.add_template(f) - self.item_names = files.get_file_list(OPT_DIR, r".*\.base$") - self.liststore = Gtk.ListStore(Pixbuf, str) - for filen in self.item_names: - pixbuf = Gtk.IconTheme.get_default().load_icon(icon, 64, 0) - self.liststore.append([pixbuf, filen]) - self.file_view.set_model(self.liststore) - filechooser.destroy() - self.file_view.unselect_all() + filechooser.set_default_filter(filefilter) + + filechooser.open_multiple(parent=self.parent, callback=self._on_add_finish) + + def _on_add_finish(self, dialog, result): + try: + picked_files = dialog.open_multiple_finish(result) + for gfile in picked_files: + files.add_template(gfile.get_path()) + self._refresh_liststore() + except GLib.Error as error: + print(f"Error opening file: {error.message}") - def on_open_clicked(self, widget): + def _on_open_clicked(self, widget): if self.current is not None: item = self.item_names[self.current] - args_list = settings['editor'].split(' ') + args_list = settings["editor"].split(" ") args_list.append(os.path.join(OPT_DIR, item)) try: Popen(args_list) - except Exception as e: + except Exception: logging.error("malformed editor command") self.current = None self.file_view.unselect_all() - def on_rm_clicked(self, widget): + def _on_rm_clicked(self, widget): if self.current is not None: item = self.item_names.pop(self.current) files.delete_template(item) - self.liststore = Gtk.ListStore(Pixbuf, str) - for filen in self.item_names: - pixbuf = Gtk.IconTheme.get_default().load_icon(icon, 64, 0) - self.liststore.append([pixbuf, filen]) - self.file_view.set_model(self.liststore) + self._refresh_liststore() self.current = None self.file_view.unselect_all() - def on_file_click(self, widget, pos): + def _on_file_click(self, widget, pos): self.current = int(str(pos)) self.sel_file = self.liststore[self.current][1] diff --git a/wpgtk/gui/theme_picker.py b/wpgtk/gui/theme_picker.py index b8e6656..2b42a2d 100755 --- a/wpgtk/gui/theme_picker.py +++ b/wpgtk/gui/theme_picker.py @@ -12,15 +12,16 @@ from gi import require_version -require_version("Gtk", "3.0") -from gi.repository import Gtk # noqa: E402 +require_version("Gtk", "4.0") +from gi.repository import Gtk, Gdk, GLib # noqa: E402 +from gi.repository.GdkPixbuf import Pixbuf # noqa: E402 PAD = 10 -class mainWindow(Gtk.Window): +class MainWindow(Gtk.Window): def __init__(self, args): - Gtk.Window.__init__(self, title="wpgtk " + __version__) + super().__init__(title="wpgtk " + __version__) image_name = os.path.join(WPG_DIR, ".current") image_name = os.path.realpath(image_name) @@ -30,59 +31,61 @@ def __init__(self, args): # these variables are just to get the image # and preview of current wallpaper file_name = themer.get_current() - logging.info("current wallpaper: " + file_name) sample_name = files.get_sample_path(file_name) + logging.info("current wallpaper: " + file_name) + + self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + util.set_uniform_margins(self.box, PAD) + self.notebook = Gtk.Notebook() - self.add(self.notebook) + self.notebook.set_vexpand(True) + self.box.append(self.notebook) + self.set_child(self.box) self.wpage = Gtk.Grid() - self.wpage.set_border_width(PAD) self.wpage.set_column_homogeneous(1) self.wpage.set_row_spacing(PAD) self.wpage.set_column_spacing(PAD) + util.set_uniform_margins(self.wpage, PAD) self.cpage = color_grid.ColorGrid(self) self.fpage = template_grid.TemplateGrid(self) self.optpage = option_grid.OptionsGrid(self) self.keypage = keyword_grid.KeywordGrid(self) - self.notebook.append_page(self.wpage, Gtk.Label("Wallpapers")) - self.notebook.append_page(self.cpage, Gtk.Label("Colors")) - self.notebook.append_page(self.fpage, Gtk.Label("Templates")) - self.notebook.append_page(self.keypage, Gtk.Label("Keywords")) - self.notebook.append_page(self.optpage, Gtk.Label("Options")) + self.notebook.append_page(self.wpage, Gtk.Label(label="Wallpapers")) + self.notebook.append_page(self.cpage, Gtk.Label(label="Colors")) + self.notebook.append_page(self.fpage, Gtk.Label(label="Templates")) + self.notebook.append_page(self.keypage, Gtk.Label(label="Keywords")) + self.notebook.append_page(self.optpage, Gtk.Label(label="Options")) - option_list = Gtk.ListStore(str) current_idx = None + self.option_combo = Gtk.ComboBoxText() + self.colorscheme = Gtk.ComboBoxText() + for i, elem in enumerate(files.get_file_list()): if elem == themer.get_current(): current_idx = i - - option_list.append([elem]) - self.option_combo = Gtk.ComboBox.new_with_model(option_list) - self.renderer_text = Gtk.CellRendererText() - self.option_combo.pack_start(self.renderer_text, True) - self.option_combo.add_attribute(self.renderer_text, "text", 0) - self.option_combo.set_entry_text_column(0) - - self.textbox = Gtk.Label() - self.textbox.set_text("Select colorscheme") - self.colorscheme = Gtk.ComboBox.new_with_model(option_list) - self.colorscheme.pack_start(self.renderer_text, True) - self.colorscheme.add_attribute(self.renderer_text, "text", 0) - self.colorscheme.set_entry_text_column(0) - - self.set_border_width(10) - self.preview = Gtk.Image() - self.sample = Gtk.Image() - - self.get_image_preview(image_name, sample_name) + self.option_combo.append_text(elem) + self.colorscheme.append_text(elem) + + self.preview = Gtk.Picture.new_for_filename(image_name) + self.preview.set_content_fit(Gtk.ContentFit.CONTAIN) + self.preview.set_vexpand(True) + + self._sample_pixbuf = None + self.sample = Gtk.DrawingArea() + self.sample.set_content_height(50) + self.sample.set_hexpand(True) + self.sample.set_vexpand(False) + self.sample.set_valign(Gtk.Align.START) + self.sample.set_draw_func(util.draw_sample, lambda: self._sample_pixbuf) + self._set_sample_file(sample_name) self.add_button = Gtk.Button(label="Add") self.set_button = Gtk.Button(label="Set") self.rm_button = Gtk.Button(label="Remove") - # adds to first cell in wpage self.wpage.attach(self.option_combo, 1, 1, 2, 1) self.wpage.attach(self.colorscheme, 1, 2, 2, 1) @@ -91,13 +94,11 @@ def __init__(self, args): self.wpage.attach(self.rm_button, 4, 1, 1, 1) self.wpage.attach(self.preview, 1, 3, 4, 1) self.wpage.attach(self.sample, 1, 4, 4, 1) - self.add_button.connect("clicked", self.on_add_clicked) - self.set_button.connect("clicked", self.on_set_clicked) - self.rm_button.connect("clicked", self.on_rm_clicked) - self.option_combo.connect("changed", self.combo_box_change) - self.colorscheme.connect("changed", self.colorscheme_box_change) - self.entry = Gtk.Entry() - self.current_walls = Gtk.ComboBox() + self.add_button.connect("clicked", self._on_add_clicked) + self.set_button.connect("clicked", self._on_set_clicked) + self.rm_button.connect("clicked", self._on_rm_clicked) + self.option_combo.connect("changed", self._combo_box_change) + self.colorscheme.connect("changed", self._colorscheme_box_change) if current_idx is not None: self.option_combo.set_active(current_idx) @@ -105,48 +106,42 @@ def __init__(self, args): self.cpage.option_combo.set_active(current_idx) self.set_button.set_sensitive(True) - def on_add_clicked(self, widget): - filechooser = Gtk.FileChooserDialog( - "Select an Image", - self, - Gtk.FileChooserAction.OPEN, - ( - Gtk.STOCK_CANCEL, - Gtk.ResponseType.CANCEL, - Gtk.STOCK_OPEN, - Gtk.ResponseType.OK, - ), - ) - - filechooser.set_select_multiple(True) + def _set_sample_file(self, path): + try: + self._sample_pixbuf = Pixbuf.new_from_file(path) + except Exception: + self._sample_pixbuf = None + self.sample.queue_draw() + + def _on_add_clicked(self, widget): + filechooser = Gtk.FileDialog() + filefilter = Gtk.FileFilter() filefilter.set_name("Images") filefilter.add_mime_type("image/png") filefilter.add_mime_type("image/jpg") filefilter.add_mime_type("image/gif") filefilter.add_mime_type("image/jpeg") - filechooser.add_filter(filefilter) - response = filechooser.run() - - if response == Gtk.ResponseType.OK: - option_list = Gtk.ListStore(str) + filechooser.set_default_filter(filefilter) - for f in filechooser.get_filenames(): - themer.create_theme(f) + filechooser.open_multiple(parent=self, callback=self._on_add_finish) - for elem in list(files.get_file_list()): - option_list.append([elem]) + def _on_add_finish(self, dialog, result): + try: + picked_files = dialog.open_multiple_finish(result) - self.option_combo.set_model(option_list) - self.option_combo.set_entry_text_column(0) - self.colorscheme.set_model(option_list) - self.colorscheme.set_entry_text_column(0) + for gfile in picked_files: + themer.create_theme(gfile.get_path()) - self.cpage.option_combo.set_model(option_list) + file_list = list(files.get_file_list()) + for combo in (self.option_combo, self.colorscheme, self.cpage.option_combo): + combo.remove_all() + for filename in file_list: + combo.append_text(filename) + except GLib.Error as error: + print(f"Error opening file: {error.message}") - filechooser.destroy() - - def on_set_clicked(self, widget): + def _on_set_clicked(self, widget): x = self.option_combo.get_active() y = self.colorscheme.get_active() current_walls = files.get_file_list() @@ -155,55 +150,39 @@ def on_set_clicked(self, widget): colorscheme_file = current_walls[y] themer.set_theme(filename, colorscheme_file) - def on_rm_clicked(self, widget): + def _on_rm_clicked(self, widget): x = self.option_combo.get_active() current_walls = files.get_file_list() if current_walls: filename = current_walls[x] themer.delete_theme(filename) - option_list = Gtk.ListStore(str) - for elem in list(files.get_file_list()): - option_list.append([elem]) - self.option_combo.set_model(option_list) - self.option_combo.set_entry_text_column(0) - self.colorscheme.set_model(option_list) - - self.cpage.option_combo.set_model(option_list) + file_list = list(files.get_file_list()) + for combo in (self.option_combo, self.colorscheme, self.cpage.option_combo): + combo.remove_all() + for elem in file_list: + combo.append_text(elem) - def combo_box_change(self, widget): + def _combo_box_change(self, widget): self.set_button.set_sensitive(True) x = self.option_combo.get_active() self.colorscheme.set_active(x) selected_file = files.get_file_list()[x] filepath = os.path.join(WALL_DIR, selected_file) - self.set_image_preview(filepath) + self.preview.set_filename(filepath) - def colorscheme_box_change(self, widget): + def _colorscheme_box_change(self, widget): x = self.colorscheme.get_active() self.cpage.option_combo.set_active(x) - # called on opening to looad the current image - def get_image_preview(self, image_name, sample_name): - pixbuf_preview = util.get_preview_pixbuf(image_name) - pixbuf_sample = util.get_sample_pixbuf(sample_name) - if pixbuf_preview is not None: - self.preview.set_from_pixbuf(pixbuf_preview) - - if pixbuf_sample is not None: - self.sample.set_from_pixbuf(pixbuf_sample) - - # called when combo box changes the selected image - def set_image_preview(self, filepath): - pixbuf_preview = util.get_preview_pixbuf(filepath) - - if pixbuf_preview is not None: - self.preview.set_from_pixbuf(pixbuf_preview) +def run(args): + app = Gtk.Application(application_id="com.deviantfero.wpgtk") + def on_activate(app): + win = MainWindow(args) + win.set_application(app) + win.present() -def run(args): - win = mainWindow(args) - win.connect("delete-event", Gtk.main_quit) - win.show_all() - Gtk.main() + app.connect("activate", on_activate) + return app.run(None) diff --git a/wpgtk/gui/util.py b/wpgtk/gui/util.py index 5d60fa6..1efff3d 100644 --- a/wpgtk/gui/util.py +++ b/wpgtk/gui/util.py @@ -3,7 +3,8 @@ import pathlib require_version("GdkPixbuf", "2.0") -from gi.repository import GdkPixbuf # noqa: E402 +require_version("Gtk", "4.0") +from gi.repository import GdkPixbuf, Gtk, Gdk # noqa: E402 def get_preview_pixbuf(image_name): @@ -61,3 +62,80 @@ def get_sample_pixbuf(sample_name): ) else: return None + + +def draw_sample(area, cr, width, height, get_pixbuf): + """ + Draw a scaled pixbuf centred horizontally into a Cairo context. + + Intended as a draw function for Gtk.DrawingArea.set_draw_func. Scales the + pixbuf to fit within the given dimensions while preserving the aspect ratio + then paints it aligned to the top and centred horizontally. + + Parameters: + - area (Gtk.DrawingArea): the drawing area widget + - cr (cairo.Context): the Cairo context to draw into + - width (int): allocated width of the drawing area + - height (int): allocated height of the drawing area + - get_pixbuf (callable): called with no arguments to retrieve the current + GdkPixbuf.Pixbuf to draw; does nothing if it returns None + + Returns: + """ + pixbuf = get_pixbuf() + if pixbuf is None: + return + img_w = pixbuf.get_width() + img_h = pixbuf.get_height() + scale = min(width / img_w, height / img_h) + dest_w = max(1, int(img_w * scale)) + dest_h = max(1, int(img_h * scale)) + scaled = pixbuf.scale_simple(dest_w, dest_h, GdkPixbuf.InterpType.BILINEAR) + x = int((width - dest_w) / 2) + Gdk.cairo_set_source_pixbuf(cr, scaled, x, 0) + cr.paint() + + +def set_widget_colors(button, background="#000", foreground="#fff"): + """ + Set a style for background and foreground on a widget + + This function applies a css provider to the widget passed as parameter + the css provider includes a basic style string that utilizes the + background and foreground paramters to change those properties on + the widget + + Parameters: + - widget (Gtk.Widget): a GTK widget instance + - background (str): background color to apply in style + - foreground (str): foreground color to apply in style + + Returns: + """ + css_provider = Gtk.CssProvider() + css = f""" + button {{ + background-color: {background}; + color: {foreground}; + }} + """ + css_provider.load_from_data(css.encode()) + button.get_style_context().add_provider( + css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + +def set_uniform_margins(widget, margin): + """ + Set uniform margins on all sides of a widget + + Parameters: + - widget (Gtk.Widget): a GTK widget instance + - margin (integer): the margin amount to apply + + Returns: + """ + widget.set_margin_top(margin) + widget.set_margin_bottom(margin) + widget.set_margin_start(margin) + widget.set_margin_end(margin)