Skip to content

Commit 6a5fc4c

Browse files
committed
feat(gui): add minimize-to-tray support with tray icon and settings toggle
Introduce system tray functionality, allowing the app window to minimize to tray instead of quitting. Added `TrayManager` for tray icon management (AppIndicator or StatusIcon), updated shutdown and quit logic to support tray, and exposed a "Minimize to system tray" toggle in settings. Config option `minimize_to_tray` added to enable/disable feature. Closes #9
1 parent 83ad702 commit 6a5fc4c

7 files changed

Lines changed: 399 additions & 14 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ install: nuitka
9494
@mkdir -p "$(DESKTOP_DEST_DIR)"
9595
@printf "%s\n" \
9696
"[Desktop Entry]" \
97-
"Version=0.3.1" \
97+
"Version=0.3.2" \
9898
"Type=Application" \
9999
"Name=Clipse GUI" \
100100
"GenericName=Clipboard Manager" \

clipse_gui/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.1"
1+
__version__ = "0.3.2"

clipse_gui/app.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
DEFAULT_WINDOW_WIDTH,
88
)
99
from .controller import ClipboardHistoryController
10+
from .tray_manager import TrayManager
1011

1112
from gi.repository import Gtk, Gio, GLib # noqa: E402
1213

@@ -23,6 +24,7 @@ def __init__(self):
2324
)
2425
self.window = None
2526
self.controller = None
27+
self.tray_manager = None
2628

2729
def do_startup(self):
2830
"""Called once when the application starts."""
@@ -56,6 +58,12 @@ def do_activate(self):
5658
self.window.set_icon_name("edit-copy")
5759
except GLib.Error as e:
5860
log.warning(f"Could not set window icon name: {e}")
61+
62+
# Setup tray manager
63+
self.tray_manager = TrayManager(self)
64+
65+
# Connect window events for tray functionality
66+
self.window.connect("delete-event", self._on_window_delete)
5967

6068
try:
6169
self.controller = ClipboardHistoryController(self.window)
@@ -99,4 +107,15 @@ def do_shutdown(self):
99107
if hasattr(self.controller, "_trigger_save"):
100108
self.controller._trigger_save()
101109

110+
# Cleanup tray resources
111+
if self.tray_manager:
112+
self.tray_manager.cleanup()
113+
102114
Gtk.Application.do_shutdown(self)
115+
116+
def _on_window_delete(self, window, event):
117+
"""Handle window delete event - minimize to tray if enabled."""
118+
if self.tray_manager and constants.MINIMIZE_TO_TRAY:
119+
if self.tray_manager.minimize_to_tray():
120+
return True # Prevent window destruction
121+
return False # Allow normal window destruction

clipse_gui/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"save_debounce_ms": "300",
2727
"search_debounce_ms": "250",
2828
"paste_simulation_delay_ms": "150",
29+
"minimize_to_tray": "True",
2930
},
3031
"Commands": {
3132
"copy_tool_cmd": "wl-copy",
@@ -76,6 +77,7 @@
7677
PASTE_SIMULATION_DELAY_MS = config.getint(
7778
"General", "paste_simulation_delay_ms", fallback=150
7879
)
80+
MINIMIZE_TO_TRAY = config.getboolean("General", "minimize_to_tray", fallback=True)
7981

8082
COPY_TOOL_CMD = config.get("Commands", "copy_tool_cmd", fallback="wl-copy")
8183
X11_COPY_TOOL_CMD = config.get(

clipse_gui/controller.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -990,28 +990,42 @@ def restart_application(self):
990990
"""Restarts the application to apply settings changes."""
991991
import sys
992992
import os
993+
import subprocess
994+
import shutil
993995

994996
log.info("Restarting application to apply settings changes...")
995-
996-
# Get the current application instance
997997
app = self.window.get_application()
998998
if app:
999-
# Close the current application
1000999
app.quit()
10011000

1002-
# Restart the application
10031001
try:
1004-
# Get the original command line arguments
1005-
args = sys.argv[:]
1006-
log.debug(f"Restarting with args: {args}")
1002+
clipse_gui_path = shutil.which("clipse-gui")
1003+
1004+
if clipse_gui_path:
1005+
args = [clipse_gui_path] + sys.argv[1:]
1006+
log.debug(f"Restarting with system executable: {args}")
1007+
subprocess.Popen(args, cwd=os.getcwd())
1008+
elif getattr(sys, "frozen", False):
1009+
executable = sys.executable
1010+
args = [executable] + sys.argv[1:]
1011+
log.debug(f"Restarting with frozen executable: {args}")
1012+
subprocess.Popen(args, cwd=os.getcwd())
1013+
else:
1014+
original_cmd = sys.argv[0]
1015+
if os.path.isfile(original_cmd) and os.access(original_cmd, os.X_OK):
1016+
args = [original_cmd] + sys.argv[1:]
1017+
log.debug(f"Restarting with original command: {args}")
1018+
subprocess.Popen(args, cwd=os.getcwd())
1019+
else:
1020+
raise Exception(f"Cannot find executable: {original_cmd}")
10071021

1008-
# Use os.execv to replace the current process
1009-
os.execv(sys.executable, [sys.executable] + args)
10101022
except Exception as e:
10111023
log.error(f"Failed to restart application: {e}")
1012-
# Fallback: just quit the application
1013-
if app:
1014-
app.quit()
1024+
1025+
if app:
1026+
app.quit()
1027+
else:
1028+
sys.exit(0)
10151029

10161030
def on_preview_key_press(self, preview_window, event):
10171031
"""Handles key presses within the preview window."""
@@ -1161,6 +1175,16 @@ def on_key_press(self, widget, event):
11611175
else:
11621176
app = self.window.get_application()
11631177
if app:
1178+
# Try to minimize to tray if enabled, otherwise quit
1179+
from . import constants
1180+
1181+
if (
1182+
hasattr(app, "tray_manager")
1183+
and app.tray_manager
1184+
and constants.MINIMIZE_TO_TRAY
1185+
):
1186+
if app.tray_manager.minimize_to_tray():
1187+
return True # Successfully minimized to tray
11641188
app.quit()
11651189
else:
11661190
log.warning("Application instance is None. Cannot quit.")
@@ -1276,6 +1300,16 @@ def on_key_press(self, widget, event):
12761300
else:
12771301
app = self.window.get_application()
12781302
if app:
1303+
# Try to minimize to tray if enabled, otherwise quit
1304+
from . import constants
1305+
1306+
if (
1307+
hasattr(app, "tray_manager")
1308+
and app.tray_manager
1309+
and constants.MINIMIZE_TO_TRAY
1310+
):
1311+
if app.tray_manager.minimize_to_tray():
1312+
return True # Successfully minimized to tray
12791313
app.quit()
12801314
else:
12811315
log.warning("Application instance is None. Cannot quit.")

0 commit comments

Comments
 (0)