Big Update
This update will need to get cleaned up, but for right now we are doing testing.
This commit is contained in:
283
ui_components.py
Normal file
283
ui_components.py
Normal file
@@ -0,0 +1,283 @@
|
||||
# UI components and dialogs for the desktop application
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
from PIL import Image, ImageTk
|
||||
import os
|
||||
import uuid
|
||||
from config import THEME
|
||||
|
||||
|
||||
class MacroDialog:
|
||||
"""Dialog for adding/editing macros"""
|
||||
|
||||
def __init__(self, parent, macro_manager, macro_id=None):
|
||||
self.parent = parent
|
||||
self.macro_manager = macro_manager
|
||||
self.macro_id = macro_id
|
||||
self.dialog = None
|
||||
self.result = None
|
||||
|
||||
def show(self):
|
||||
"""Show the dialog and return the result"""
|
||||
self.dialog = tk.Toplevel(self.parent)
|
||||
self.dialog.title("Edit Macro" if self.macro_id else "Add Macro")
|
||||
self.dialog.geometry("450x400")
|
||||
self.dialog.transient(self.parent)
|
||||
self.dialog.configure(bg=THEME['bg_color'])
|
||||
self.dialog.grab_set()
|
||||
|
||||
# If editing, get existing macro data
|
||||
if self.macro_id:
|
||||
macro = self.macro_manager.macros.get(self.macro_id, {})
|
||||
else:
|
||||
macro = {}
|
||||
|
||||
self._create_widgets(macro)
|
||||
|
||||
# Wait for dialog to close
|
||||
self.dialog.wait_window()
|
||||
return self.result
|
||||
|
||||
def _create_widgets(self, macro):
|
||||
"""Create the dialog widgets"""
|
||||
# Name
|
||||
tk.Label(self.dialog, text="Macro Name:", bg=THEME['bg_color'], fg=THEME['fg_color']).grid(row=0, column=0, padx=5, pady=5, sticky="w")
|
||||
self.name_entry = tk.Entry(self.dialog, width=30, bg=THEME['highlight_color'], fg=THEME['fg_color'], insertbackground=THEME['fg_color'])
|
||||
self.name_entry.grid(row=0, column=1, padx=5, pady=5)
|
||||
self.name_entry.insert(0, macro.get("name", ""))
|
||||
|
||||
# Category
|
||||
tk.Label(self.dialog, text="Category:", bg=THEME['bg_color'], fg=THEME['fg_color']).grid(row=1, column=0, padx=5, pady=5, sticky="w")
|
||||
self.category_entry = tk.Entry(self.dialog, width=30, bg=THEME['highlight_color'], fg=THEME['fg_color'], insertbackground=THEME['fg_color'])
|
||||
self.category_entry.grid(row=1, column=1, padx=5, pady=5)
|
||||
self.category_entry.insert(0, macro.get("category", ""))
|
||||
|
||||
# Type
|
||||
tk.Label(self.dialog, text="Type:", bg=THEME['bg_color'], fg=THEME['fg_color']).grid(row=2, column=0, padx=5, pady=5, sticky="w")
|
||||
self.type_var = tk.StringVar(value=macro.get("type", "text"))
|
||||
|
||||
radio_style = {'bg': THEME['bg_color'], 'fg': THEME['fg_color'], 'selectcolor': THEME['accent_color']}
|
||||
tk.Radiobutton(self.dialog, text="Text", variable=self.type_var, value="text", **radio_style).grid(row=2, column=1, sticky="w")
|
||||
tk.Radiobutton(self.dialog, text="Application", variable=self.type_var, value="app", **radio_style).grid(row=3, column=1, sticky="w")
|
||||
|
||||
# Command/Text
|
||||
tk.Label(self.dialog, text="Command/Text:", bg=THEME['bg_color'], fg=THEME['fg_color']).grid(row=4, column=0, padx=5, pady=5, sticky="w")
|
||||
self.command_text = tk.Text(self.dialog, width=30, height=5, bg=THEME['highlight_color'], fg=THEME['fg_color'], insertbackground=THEME['fg_color'])
|
||||
self.command_text.grid(row=4, column=1, padx=5, pady=5)
|
||||
self.command_text.insert("1.0", macro.get("command", ""))
|
||||
|
||||
# Modifiers
|
||||
self._create_modifiers(macro)
|
||||
|
||||
# Image
|
||||
self.image_path = tk.StringVar()
|
||||
tk.Label(self.dialog, text="Image:", bg=THEME['bg_color'], fg=THEME['fg_color']).grid(row=6, column=0, padx=5, pady=5, sticky="w")
|
||||
image_entry = tk.Entry(self.dialog, textvariable=self.image_path, width=30, bg=THEME['highlight_color'], fg=THEME['fg_color'], insertbackground=THEME['fg_color'])
|
||||
image_entry.grid(row=6, column=1, padx=5, pady=5)
|
||||
|
||||
button_style = {'bg': THEME['button_bg'], 'fg': THEME['button_fg'], 'activebackground': THEME['accent_color'],
|
||||
'activeforeground': THEME['button_fg'], 'bd': 0, 'relief': tk.FLAT}
|
||||
|
||||
tk.Button(self.dialog, text="Browse...", command=self._browse_image, **button_style).grid(row=6, column=2)
|
||||
|
||||
# Buttons
|
||||
tk.Button(self.dialog, text="Save", command=self._save, **button_style).grid(row=7, column=0, padx=5, pady=20)
|
||||
tk.Button(self.dialog, text="Cancel", command=self._cancel, **button_style).grid(row=7, column=1, padx=5, pady=20)
|
||||
|
||||
def _create_modifiers(self, macro):
|
||||
"""Create modifier checkboxes"""
|
||||
mod_frame = tk.Frame(self.dialog, bg=THEME['bg_color'])
|
||||
mod_frame.grid(row=5, column=0, columnspan=2, sticky="w", padx=5, pady=5)
|
||||
|
||||
tk.Label(mod_frame, text="Key Modifiers:", bg=THEME['bg_color'], fg=THEME['fg_color']).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
modifiers = macro.get("modifiers", {})
|
||||
self.ctrl_var = tk.BooleanVar(value=modifiers.get("ctrl", False))
|
||||
self.alt_var = tk.BooleanVar(value=modifiers.get("alt", False))
|
||||
self.shift_var = tk.BooleanVar(value=modifiers.get("shift", False))
|
||||
self.enter_var = tk.BooleanVar(value=modifiers.get("enter", False))
|
||||
|
||||
checkbox_style = {'bg': THEME['bg_color'], 'fg': THEME['fg_color'], 'selectcolor': THEME['accent_color'],
|
||||
'activebackground': THEME['bg_color'], 'activeforeground': THEME['fg_color']}
|
||||
|
||||
tk.Checkbutton(mod_frame, text="Ctrl", variable=self.ctrl_var, **checkbox_style).pack(side=tk.LEFT, padx=5)
|
||||
tk.Checkbutton(mod_frame, text="Alt", variable=self.alt_var, **checkbox_style).pack(side=tk.LEFT, padx=5)
|
||||
tk.Checkbutton(mod_frame, text="Shift", variable=self.shift_var, **checkbox_style).pack(side=tk.LEFT, padx=5)
|
||||
tk.Checkbutton(mod_frame, text="Add Enter", variable=self.enter_var, **checkbox_style).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
def _browse_image(self):
|
||||
"""Browse for image file"""
|
||||
filename = filedialog.askopenfilename(
|
||||
filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")])
|
||||
if filename:
|
||||
self.image_path.set(filename)
|
||||
|
||||
def _save(self):
|
||||
"""Save the macro"""
|
||||
name = self.name_entry.get().strip()
|
||||
if not name:
|
||||
messagebox.showerror("Error", "Macro name is required")
|
||||
return
|
||||
|
||||
macro_type = self.type_var.get()
|
||||
command = self.command_text.get("1.0", tk.END).strip()
|
||||
category = self.category_entry.get().strip()
|
||||
|
||||
modifiers = {
|
||||
"ctrl": self.ctrl_var.get(),
|
||||
"alt": self.alt_var.get(),
|
||||
"shift": self.shift_var.get(),
|
||||
"enter": self.enter_var.get()
|
||||
}
|
||||
|
||||
if self.macro_id:
|
||||
# Update existing macro
|
||||
success = self.macro_manager.update_macro(
|
||||
self.macro_id, name, macro_type, command, category, modifiers, self.image_path.get())
|
||||
else:
|
||||
# Add new macro
|
||||
self.macro_id = self.macro_manager.add_macro(
|
||||
name, macro_type, command, category, modifiers, self.image_path.get())
|
||||
success = bool(self.macro_id)
|
||||
|
||||
if success:
|
||||
self.result = self.macro_id
|
||||
self.dialog.destroy()
|
||||
else:
|
||||
messagebox.showerror("Error", "Failed to save macro")
|
||||
|
||||
def _cancel(self):
|
||||
"""Cancel dialog"""
|
||||
self.result = None
|
||||
self.dialog.destroy()
|
||||
|
||||
|
||||
class MacroSelector:
|
||||
"""Dialog for selecting a macro from a list"""
|
||||
|
||||
def __init__(self, parent, macro_manager, title="Select Macro"):
|
||||
self.parent = parent
|
||||
self.macro_manager = macro_manager
|
||||
self.title = title
|
||||
self.result = None
|
||||
|
||||
def show(self):
|
||||
"""Show the selection dialog"""
|
||||
if not self.macro_manager.macros:
|
||||
messagebox.showinfo("No Macros", "There are no macros available.")
|
||||
return None
|
||||
|
||||
dialog = tk.Toplevel(self.parent)
|
||||
dialog.title(self.title)
|
||||
dialog.geometry("200x340")
|
||||
dialog.transient(self.parent)
|
||||
dialog.configure(bg=THEME['bg_color'])
|
||||
dialog.grab_set()
|
||||
|
||||
# Instructions
|
||||
tk.Label(dialog, text=f"{self.title}:", bg=THEME['bg_color'], fg=THEME['fg_color']).pack(pady=5)
|
||||
|
||||
# Listbox
|
||||
listbox = tk.Listbox(dialog, bg=THEME['highlight_color'], fg=THEME['fg_color'], selectbackground=THEME['accent_color'])
|
||||
listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
|
||||
|
||||
# Populate listbox
|
||||
macro_ids = []
|
||||
for macro_id, macro in self.macro_manager.macros.items():
|
||||
listbox.insert(tk.END, macro["name"])
|
||||
macro_ids.append(macro_id)
|
||||
|
||||
def on_select():
|
||||
if not listbox.curselection():
|
||||
messagebox.showwarning("No Selection", f"Please select a macro.")
|
||||
return
|
||||
idx = listbox.curselection()[0]
|
||||
self.result = macro_ids[idx]
|
||||
dialog.destroy()
|
||||
|
||||
button_style = {'bg': THEME['button_bg'], 'fg': THEME['button_fg'], 'activebackground': THEME['accent_color'],
|
||||
'activeforeground': THEME['button_fg'], 'bd': 0, 'relief': tk.FLAT}
|
||||
|
||||
tk.Button(dialog, text="Select", command=on_select, **button_style).pack(pady=10)
|
||||
tk.Button(dialog, text="Cancel", command=lambda: dialog.destroy(), **button_style).pack(pady=5)
|
||||
|
||||
dialog.wait_window()
|
||||
return self.result
|
||||
|
||||
|
||||
class TabManager:
|
||||
"""Dialog for managing macro categories/tabs"""
|
||||
|
||||
def __init__(self, parent, macro_manager):
|
||||
self.parent = parent
|
||||
self.macro_manager = macro_manager
|
||||
|
||||
def show(self):
|
||||
"""Show tab management dialog"""
|
||||
dialog = tk.Toplevel(self.parent)
|
||||
dialog.title("Manage Tabs")
|
||||
dialog.geometry("400x300")
|
||||
dialog.transient(self.parent)
|
||||
dialog.configure(bg=THEME['bg_color'])
|
||||
dialog.grab_set()
|
||||
|
||||
# Instructions
|
||||
tk.Label(dialog, text="Assign categories to macros for better organization:",
|
||||
bg=THEME['bg_color'], fg=THEME['fg_color']).pack(pady=10)
|
||||
|
||||
# Create scrollable frame
|
||||
list_frame = ttk.Frame(dialog)
|
||||
list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
|
||||
|
||||
canvas = tk.Canvas(list_frame, bg=THEME['bg_color'], highlightthickness=0)
|
||||
scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=canvas.yview)
|
||||
scrollable_frame = ttk.Frame(canvas)
|
||||
|
||||
scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
)
|
||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# Category entries for each macro
|
||||
category_vars = {}
|
||||
for macro_id, macro in self.macro_manager.macros.items():
|
||||
frame = ttk.Frame(scrollable_frame)
|
||||
frame.pack(fill="x", pady=2, padx=5)
|
||||
|
||||
tk.Label(frame, text=macro["name"], bg=THEME['bg_color'], fg=THEME['fg_color'],
|
||||
width=20, anchor="w").pack(side=tk.LEFT)
|
||||
|
||||
category_var = tk.StringVar(value=macro.get("category", ""))
|
||||
category_vars[macro_id] = category_var
|
||||
entry = tk.Entry(frame, textvariable=category_var, width=15,
|
||||
bg=THEME['highlight_color'], fg=THEME['fg_color'], insertbackground=THEME['fg_color'])
|
||||
entry.pack(side=tk.RIGHT, padx=(5, 0))
|
||||
|
||||
# Buttons
|
||||
button_frame = ttk.Frame(dialog)
|
||||
button_frame.pack(fill=tk.X, pady=10)
|
||||
|
||||
def save_categories():
|
||||
for macro_id, category_var in category_vars.items():
|
||||
category = category_var.get().strip()
|
||||
if category:
|
||||
self.macro_manager.macros[macro_id]["category"] = category
|
||||
else:
|
||||
self.macro_manager.macros[macro_id].pop("category", None)
|
||||
|
||||
self.macro_manager.save_macros()
|
||||
dialog.destroy()
|
||||
|
||||
button_style = {'bg': THEME['button_bg'], 'fg': THEME['button_fg'], 'activebackground': THEME['accent_color'],
|
||||
'activeforeground': THEME['button_fg'], 'bd': 0, 'relief': tk.FLAT}
|
||||
|
||||
tk.Button(button_frame, text="Save", command=save_categories, **button_style).pack(side=tk.LEFT, padx=5)
|
||||
tk.Button(button_frame, text="Cancel", command=lambda: dialog.destroy(), **button_style).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
dialog.wait_window()
|
||||
Reference in New Issue
Block a user