From 57291952ea608bb80d5132825e9fd9198fd26cb4 Mon Sep 17 00:00:00 2001 From: r37r05p3C7 <153987701+r37r05p3C7@users.noreply.github.com> Date: Mon, 18 Mar 2024 01:23:29 +0200 Subject: [PATCH 1/2] Implemented filepicker type higlighting --- modules/callbacks.py | 1 + modules/db.py | 1 + modules/filepicker.py | 115 ++++++++++++++++++++++++++++++------------ modules/gui.py | 20 +++++--- modules/structs.py | 16 +++--- 5 files changed, 109 insertions(+), 44 deletions(-) diff --git a/modules/callbacks.py b/modules/callbacks.py index 0dfe0ff0..e9fc36f6 100644 --- a/modules/callbacks.py +++ b/modules/callbacks.py @@ -123,6 +123,7 @@ def popup_content(): start_dir /= best_match utils.push_popup(filepicker.FilePicker( title=f"Select or drop executable for {game.name}", + picker_type=filepicker.PickerType.Execs, start_dir=start_dir, callback=select_callback, buttons=[use_uri] diff --git a/modules/db.py b/modules/db.py index 23d65fa2..484d75ed 100644 --- a/modules/db.py +++ b/modules/db.py @@ -205,6 +205,7 @@ async def connect(): "style_corner_radius": f'INTEGER DEFAULT {DefaultStyle.corner_radius}', "style_text": f'TEXT DEFAULT "{DefaultStyle.text}"', "style_text_dim": f'TEXT DEFAULT "{DefaultStyle.text_dim}"', + "style_filepicker_highlight": f'TEXT DEFAULT "{DefaultStyle.filepicker_highlight}"', "tags_highlights": f'TEXT DEFAULT "{{}}"', "timestamp_format": f'TEXT DEFAULT "%d/%m/%Y %H:%M"', "use_parser_processes": f'INTEGER DEFAULT {int(True)}', diff --git a/modules/filepicker.py b/modules/filepicker.py index a4800c98..9cc58df5 100644 --- a/modules/filepicker.py +++ b/modules/filepicker.py @@ -1,4 +1,5 @@ # https://gist.github.com/Willy-JL/82137493896d385a74d148534691b6e1 +from enum import Enum, auto import pathlib import typing import string @@ -14,13 +15,49 @@ utils, # added ) # added -dir_icon = f"{icons.folder_outline} " # changed -file_icon = f"{icons.file_outline} " # changed +dir_icon = f"{icons.folder}" # changed +file_icon = f"{icons.file_outline}" # changed up_icon = icons.arrow_up # changed refresh_icon = icons.refresh # changed cancel_icon = f"{icons.cancel} Cancel" # changed ok_icon = f"{icons.check} Ok" # changed +class PickerType(Enum): + Dirs = auto() + Execs = auto() + Media = auto() + Bookmarks = auto() + +class ListItem: + def __init__(self, path: pathlib.Path, picker_type: PickerType | None): + self.path = path + self.name = path.name + self.ptype = picker_type + self.is_dir = path.is_dir() + self.is_file = path.is_file() + if self.is_dir: + self.icon = dir_icon + self.color = globals.settings.style_text + else: + self.icon, self.color = self.get_file_decorations() + + def get_file_decorations(self) -> tuple[str, tuple[float, ...]]: + if (ext := self.path.suffix) and self.ptype: + if self.ptype == PickerType.Execs and ext in (".exe", ".sh", ".swf", ".html", ".jar"): + return icons.file_cog_outline, globals.settings.style_filepicker_highlight + if self.ptype == PickerType.Media and ext in (".png", ".jpg", ".jpeg", ".webp"): + return icons.image_outline, globals.settings.style_filepicker_highlight + if self.ptype == PickerType.Media and ext in (".gif"): + return icons.video_outline, globals.settings.style_filepicker_highlight + if self.ptype == PickerType.Bookmarks and ext == ".html": + return icons.star_outline, globals.settings.style_filepicker_highlight + return file_icon, globals.settings.style_text + + def display(self): + return f"{self.icon} {self.name}" + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.name == other.name class FilePicker: flags = ( @@ -34,13 +71,13 @@ class FilePicker: def __init__( self, title="File picker", - dir_picker=False, + picker_type: PickerType | None = None, start_dir: str | pathlib.Path = None, callback: typing.Callable = None, buttons: list[str] = [], - custom_popup_flags=0 + custom_popup_flags=0, ): - self.current = 0 + self.current = -1 self.title = title self.active = True self.elapsed = 0.0 @@ -49,11 +86,13 @@ def __init__( self.selected: str = None self.filter_box_text = "" self.update_filter = False - self.items: list[str] = [] - self.dir_picker = dir_picker + self.items: list[ListItem] = [] self.dir: pathlib.Path = None + self.error: str | None = None + self.picker_type = picker_type self.flags = custom_popup_flags or self.flags self.windows = sys.platform.startswith("win") + self.dir_picker = picker_type == PickerType.Dirs if self.windows: self.drives: list[str] = [] self.current_drive = 0 @@ -76,19 +115,20 @@ def refresh(self): if self.current != -1: selected = self.items[self.current] else: - selected = "" + selected = None + self.error = None self.items.clear() try: - items = list(filter(lambda item: self.filter_box_text.lower() in item.name.lower(), self.dir.iterdir())) - if len(items) > 0: - items.sort(key=lambda item: item.name.lower()) # Sort alphabetically - items.sort(key=lambda item: item.is_dir(), reverse=True) # Sort dirs first - for item in items: - self.items.append((dir_icon if item.is_dir() else file_icon) + item.name) + paths = list(filter(lambda path: self.filter_box_text.lower() in path.name.lower(), self.dir.iterdir())) + if len(paths) > 0: + paths.sort(key=lambda path: path.name.lower()) # Sort alphabetically + paths.sort(key=lambda path: path.is_dir(), reverse=True) # Sort dirs first + for path in paths: + self.items.append(ListItem(path, self.picker_type)) else: - self.items.append("No items match your filter!" if self.filter_box_text else "This folder is empty!") + self.error = "No items match your filter!" if self.filter_box_text else "This folder is empty!" except Exception: - self.items.append("Cannot open this folder!") + self.error = "Cannot open this folder!" if self.windows: self.drives.clear() i = -1 @@ -161,20 +201,33 @@ def tick(self, popup_uuid: str = ""): width = imgui.get_item_rect_size().x # Main list - imgui.set_next_item_width(width) - imgui.push_style_color(imgui.COLOR_HEADER, *style.colors[imgui.COLOR_BUTTON_HOVERED]) # added - _, value = imgui.listbox(f"###file_list", self.current, self.items, (size.y * 0.65) / imgui.get_frame_height()) - imgui.pop_style_color() # added - if value != -1: - self.current = min(max(value, 0), len(self.items) - 1) + height = size.y * 0.65 + if self.error: + with imgui.begin_child("###file_list_error", height=height, border=True): + text_size = imgui.calc_text_size(self.error) + imgui.set_cursor_pos_x((width - text_size.x) / 2) + imgui.set_cursor_pos_y(imgui.get_cursor_pos_y() + (height / 2) - text_size.y) + imgui.text(self.error) + else: + imgui.set_next_item_width(width) + imgui.push_style_color(imgui.COLOR_HEADER, *style.colors[imgui.COLOR_BUTTON_HOVERED]) # added + with imgui.begin_list_box(f"###file_list", height=height) as listbox: + if listbox.opened: + for i, item in enumerate(self.items): + imgui.push_style_color(imgui.COLOR_TEXT, *item.color) + if imgui.selectable(item.display(), self.current == i)[0]: + self.current = i + imgui.pop_style_color() + imgui.pop_style_color() # added + if self.current != -1: + self.current = min(max(self.current, 0), len(self.items) - 1) item = self.items[self.current] - is_dir = item.startswith(dir_icon) - is_file = item.startswith(file_icon) + is_dir, is_file = item.is_dir, item.is_file if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(): if is_dir: - self.goto(self.dir / item[len(dir_icon):]) + self.goto(item.path) elif is_file and not self.dir_picker: - self.selected = str(self.dir / item[len(file_icon):]) + self.selected = str(item.path) imgui.close_current_popup() closed = True # added else: @@ -198,10 +251,10 @@ def tick(self, popup_uuid: str = ""): imgui.internal.push_item_flag(imgui.internal.ITEM_DISABLED, True) imgui.push_style_var(imgui.STYLE_ALPHA, style.alpha * 0.5) if imgui.button(ok_icon): - if value == -1: + if self.current == -1: self.selected = str(self.dir) else: - self.selected = str(self.dir / item[len(dir_icon if self.dir_picker else file_icon):]) + self.selected = str(item.path) imgui.close_current_popup() closed = True # added if not (is_file and not self.dir_picker) and not (is_dir and self.dir_picker): @@ -211,10 +264,10 @@ def tick(self, popup_uuid: str = ""): imgui.same_line() prev_pos_x = imgui.get_cursor_pos_x() if (is_file and not self.dir_picker) or (is_dir and self.dir_picker): - if value == -1: + if self.current == -1: imgui.text(f"Selected: {self.dir.name}") else: - imgui.text(f"Selected: {item[len(dir_icon if self.dir_picker else file_icon):]}") + imgui.text(f"Selected: {item.name}") # Filter bar if imgui.is_topmost() and not imgui.is_any_item_active() and (globals.gui.input_chars or any(io.keys_down)): # added if imgui.is_key_pressed(glfw.KEY_BACKSPACE): # added @@ -257,7 +310,7 @@ def __init__( ): super().__init__( title=title, - dir_picker=True, + picker_type=PickerType.Dirs, start_dir=start_dir, callback=callback, buttons=buttons, diff --git a/modules/gui.py b/modules/gui.py index e1f83562..bb0f1d1f 100644 --- a/modules/gui.py +++ b/modules/gui.py @@ -1913,6 +1913,7 @@ def select_callback(selected): game.set_image_sync(pathlib.Path(selected).read_bytes()) utils.push_popup(filepicker.FilePicker( title=f"Select or drop image for {game.name}", + picker_type=filepicker.PickerType.Media, callback=select_callback ).tick) if imgui.selectable(f"{icons.trash_can_outline} Reset image", False)[0]: @@ -3891,6 +3892,7 @@ def callback(selected: str): async_thread.run(db.update_settings("browser_custom_executable")) utils.push_popup(filepicker.FilePicker( title="Select or drop browser executable", + picker_type=filepicker.PickerType.Execs, start_dir=set.browser_custom_executable, callback=callback ).tick) @@ -4278,6 +4280,7 @@ def callback(selected): buttons={ f"{icons.check} Ok": lambda: utils.push_popup(filepicker.FilePicker( title="Select or drop bookmark file", + picker_type=filepicker.PickerType.Bookmarks, callback=callback ).tick), f"{icons.cancel} Cancel": None @@ -4574,15 +4577,19 @@ def select_callback(selected): draw_settings_label("Text dim:") draw_settings_color("style_text_dim") + draw_settings_label("Filepicker highlight:") + draw_settings_color("style_filepicker_highlight") + draw_settings_label("Defaults:") if imgui.button("Restore", width=right_width): set.style_corner_radius = DefaultStyle.corner_radius - set.style_accent = colors.hex_to_rgba_0_1(DefaultStyle.accent) - set.style_alt_bg = colors.hex_to_rgba_0_1(DefaultStyle.alt_bg) - set.style_bg = colors.hex_to_rgba_0_1(DefaultStyle.bg) - set.style_border = colors.hex_to_rgba_0_1(DefaultStyle.border) - set.style_text = colors.hex_to_rgba_0_1(DefaultStyle.text) - set.style_text_dim = colors.hex_to_rgba_0_1(DefaultStyle.text_dim) + set.style_accent = colors.hex_to_rgba_0_1(DefaultStyle.accent) + set.style_alt_bg = colors.hex_to_rgba_0_1(DefaultStyle.alt_bg) + set.style_bg = colors.hex_to_rgba_0_1(DefaultStyle.bg) + set.style_border = colors.hex_to_rgba_0_1(DefaultStyle.border) + set.style_text = colors.hex_to_rgba_0_1(DefaultStyle.text) + set.style_text_dim = colors.hex_to_rgba_0_1(DefaultStyle.text_dim) + set.style_filepicker_highlight = colors.hex_to_rgba_0_1(DefaultStyle.filepicker_highlight) self.refresh_styles() async_thread.run(db.update_settings( "style_corner_radius", @@ -4592,6 +4599,7 @@ def select_callback(selected): "style_border", "style_text", "style_text_dim", + "style_filepicker_highlight", )) imgui.end_table() diff --git a/modules/structs.py b/modules/structs.py index e9b2342e..43a3d41a 100644 --- a/modules/structs.py +++ b/modules/structs.py @@ -171,13 +171,14 @@ def format(self): class DefaultStyle: - accent = "#d4202e" - alt_bg = "#101010" - bg = "#0a0a0a" - border = "#454545" - corner_radius = 6 - text = "#ffffff" - text_dim = "#808080" + accent = "#d4202e" + alt_bg = "#101010" + bg = "#0a0a0a" + border = "#454545" + corner_radius = 6 + text = "#ffffff" + text_dim = "#808080" + filepicker_highlight = "#80FF00" @dataclasses.dataclass @@ -675,6 +676,7 @@ class Settings: style_corner_radius : int style_text : tuple[float] style_text_dim : tuple[float] + style_filepicker_highlight : tuple[float] tags_highlights : dict[Tag, TagHighlight] timestamp_format : str use_parser_processes : bool From 54ad751c713d72389813db2b97e725ca8976ffec Mon Sep 17 00:00:00 2001 From: r37r05p3C7 <153987701+r37r05p3C7@users.noreply.github.com> Date: Fri, 29 Mar 2024 08:29:20 +0200 Subject: [PATCH 2/2] Fixed unreadable colors --- modules/filepicker.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/filepicker.py b/modules/filepicker.py index 9cc58df5..30cd3bcd 100644 --- a/modules/filepicker.py +++ b/modules/filepicker.py @@ -210,7 +210,10 @@ def tick(self, popup_uuid: str = ""): imgui.text(self.error) else: imgui.set_next_item_width(width) - imgui.push_style_color(imgui.COLOR_HEADER, *style.colors[imgui.COLOR_BUTTON_HOVERED]) # added + header_color = (*style.colors[imgui.COLOR_BUTTON_HOVERED][:3], 0.3) + imgui.push_style_color(imgui.COLOR_HEADER, *header_color) # added + imgui.push_style_color(imgui.COLOR_HEADER_HOVERED, *header_color) # added + imgui.push_style_color(imgui.COLOR_HEADER_ACTIVE, *header_color) # added with imgui.begin_list_box(f"###file_list", height=height) as listbox: if listbox.opened: for i, item in enumerate(self.items): @@ -218,7 +221,7 @@ def tick(self, popup_uuid: str = ""): if imgui.selectable(item.display(), self.current == i)[0]: self.current = i imgui.pop_style_color() - imgui.pop_style_color() # added + imgui.pop_style_color(3) # added if self.current != -1: self.current = min(max(self.current, 0), len(self.items) - 1) item = self.items[self.current]