added clock, pager, and task list widgets for ref; added footer container; added option for key combo o universally toggle window with key combo if set

This commit is contained in:
2025-11-29 23:22:03 -06:00
parent 4c25ce297c
commit 22736147e6
15 changed files with 327 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ from gi.repository import Gtk
# Application imports
from .header_container import HeaderContainer
from .body_container import BodyContainer
from .footer_container import FooterContainer
@@ -39,6 +40,7 @@ class BaseContainer(Gtk.Box):
def _load_widgets(self):
self.add(HeaderContainer())
self.add(BodyContainer())
self.add(FooterContainer())
def _update_transparency(self):
self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}")

View File

@@ -26,6 +26,10 @@ class CenterContainer(Gtk.Box):
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_hexpand(True)
self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("center-container")

View File

@@ -0,0 +1,41 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class FooterContainer(Gtk.Box):
def __init__(self):
super(FooterContainer, self).__init__()
self.ctx = self.get_style_context()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
self.ctx.add_class("footer-container")
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
...

View File

@@ -22,11 +22,14 @@ class HeaderContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show_all()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
self.ctx.add_class("header-container")
def _setup_signals(self):

View File

@@ -23,6 +23,9 @@ class LeftContainer(Gtk.Box):
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("left-container")

View File

@@ -24,6 +24,9 @@ class RightContainer(Gtk.Box):
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_vexpand(True)
ctx = self.get_style_context()
ctx.add_class("right-container")

View File

@@ -104,4 +104,4 @@ class BaseControllerData:
proc = subprocess.Popen(command, stdin = subprocess.PIPE)
proc.stdin.write(data.encode(encoding))
proc.stdin.close()
retcode = proc.wait()
retcode = proc.wait()

View File

@@ -0,0 +1,110 @@
# Python imports
from datetime import datetime
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GObject
# Application imports
class CalendarWidget(Gtk.Popover):
def __init__(self):
super(CalendarWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.body = Gtk.Calendar()
self.body.show()
self.add(self.body)
class ClockWidget(Gtk.EventBox):
def __init__(self):
super(ClockWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_size_request(180, -1)
ctx = self.get_style_context()
ctx.add_class("clock-widget")
def _setup_signals(self):
self.connect("button_release_event", self._toggle_cal_popover)
def _subscribe_to_events(self):
...
def _load_widgets(self):
self.calendar = CalendarWidget()
self.label = Gtk.Label()
self.calendar.set_relative_to(self)
self.label.set_justify(Gtk.Justification.CENTER)
self.label.set_margin_top(15)
self.label.set_margin_bottom(15)
self.label.set_margin_left(15)
self.label.set_margin_right(15)
self._update_face()
self.add(self.label)
GObject.timeout_add(59000, self._update_face)
def _update_face(self):
dt_now = datetime.now()
hours_mins_sec = dt_now.strftime("%I:%M %p")
month_day_year = dt_now.strftime("%m/%d/%Y")
time_str = hours_mins_sec + "\n" + month_day_year
self.label.set_label(time_str)
def _toggle_cal_popover(self, widget, eve):
if (self.calendar.get_visible() == True):
self.calendar.popdown()
return
now = datetime.now()
timeStr = now.strftime("%m/%d/%Y")
parts = timeStr.split("/")
month = int(parts[0]) - 1
day = int(parts[1])
year = int(parts[2])
self.calendar.body.select_day(day)
self.calendar.body.select_month(month, year)
self.calendar.popup()

View File

@@ -0,0 +1,35 @@
# Python imports
# Lib imports
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck
# Application imports
class PagerWidget:
def __init__(self):
super(PagerWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
...
def get_widget(self):
return Wnck.Pager.new()

View File

@@ -0,0 +1,54 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Wnck', '3.0')
from gi.repository import Gtk
from gi.repository import Wnck
# Application imports
class TaskListWidget(Gtk.ScrolledWindow):
def __init__(self):
super(TaskListWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_hexpand(False)
self.set_size_request(180, -1)
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
viewport = Gtk.Viewport()
task_list = Wnck.Tasklist.new()
vbox = Gtk.Box()
vbox.set_orientation(Gtk.Orientation.VERTICAL)
task_list.set_scroll_enabled(False)
task_list.set_button_relief(2) # 0 = normal relief, 2 = no relief
task_list.set_grouping(1) # 0 = mever group, 1 auto group, 2 = always group
task_list.set_vexpand(True)
task_list.set_include_all_workspaces(False)
task_list.set_orientation(1) # 0 = horizontal, 1 = vertical
vbox.add(task_list)
viewport.add(vbox)
self.add(viewport)

View File

@@ -10,6 +10,11 @@ from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
try:
from gi.repository import GdkX11
except ImportError:
logger.debug("Could not import X11 gir module...")
# Application imports
from libs.status_icon import StatusIcon
from core.controllers.base_controller import BaseController
@@ -31,6 +36,9 @@ class Window(Gtk.ApplicationWindow):
self._status_icon = None
self._controller = None
self.guake_key = settings_manager.get_guake_key()
self.hidefunc = None
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
@@ -38,6 +46,7 @@ class Window(Gtk.ApplicationWindow):
self._set_window_data()
self._set_size_constraints()
self._setup_window_toggle_event()
self.show()
@@ -45,6 +54,9 @@ class Window(Gtk.ApplicationWindow):
def _setup_styling(self):
self.set_title(f"{APP_NAME}")
self.set_icon_from_file( settings_manager.get_window_icon() )
self.set_decorated(True)
self.set_skip_pager_hint(False)
self.set_skip_taskbar_hint(False)
self.set_gravity(5) # 5 = CENTER
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
@@ -74,6 +86,15 @@ class Window(Gtk.ApplicationWindow):
self.add( self._controller.get_base_container() )
def _display_manager(self):
""" Try to detect which display manager we are running under... """
import os
if os.environ.get('WAYLAND_DISPLAY'):
return 'WAYLAND'
return 'X11'
def _set_size_constraints(self):
_window_x = settings.config.main_window_x
_window_y = settings.config.main_window_y
@@ -118,6 +139,48 @@ class Window(Gtk.ApplicationWindow):
def _load_interactive_debug(self):
self.set_interactive_debugging(True)
def _setup_window_toggle_event(self) -> None:
hidebound = None
if not self.guake_key or not self._display_manager() == 'X11':
return
try:
import gi
gi.require_version('Keybinder', '3.0')
from gi.repository import Keybinder
Keybinder.init()
Keybinder.set_use_cooked_accelerators(False)
except (ImportError, ValueError) as e:
logger.warning(e)
logger.warning('Unable to load Keybinder module. This means the hide_window shortcut will be unavailable')
return
# Attempt to grab a global hotkey for hiding the window.
# If we fail, we'll never hide the window, iconifying instead.
try:
hidebound = Keybinder.bind(self.guake_key, self._on_toggle_window, self)
except (KeyError, NameError) as e:
logger.warning(e)
if not hidebound:
logger.debug('Unable to bind hide_window key, another instance/window has it.')
self.hidefunc = self.iconify
else:
self.hidefunc = self.hide
def _on_toggle_window(self, data, window):
"""Handle a request to hide/show the window"""
if not window.get_property('visible'):
window.show()
# Note: Needed to properly grab widget focus when set_skip_taskbar_hint set to True
window.present()
# NOTE: Need here to enforce sticky after hide and reshow.
window.stick()
else:
self.hidefunc()
def start(self):
Gtk.main()

View File

@@ -16,7 +16,7 @@ class IPCServer(Singleton):
""" Create a listener so that other {APP_NAME} instances send requests back to existing instance. """
def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"):
self.is_ipc_alive = False
self._ipc_port = 4848
self._ipc_port = 0 # Use 0 to let Listener chose port
self._ipc_address = ipc_address
self._conn_type = conn_type
self._ipc_authkey = b'' + bytes(f'{APP_NAME}-ipc', 'utf-8')

View File

@@ -91,7 +91,9 @@ class SettingsManager(StartCheckMixin, Singleton):
try:
with open(self._KEY_BINDINGS_FILE) as file:
bindings = json.load(file)["keybindings"]
bindings = json.load(file)["keybindings"]
self._guake_key = bindings["guake_key"]
keybindings.configure(bindings)
except Exception as e:
print( f"Settings Manager: {self._KEY_BINDINGS_FILE}\n\t\t{repr(e)}" )
@@ -155,6 +157,7 @@ class SettingsManager(StartCheckMixin, Singleton):
def get_window_icon(self) -> str: return self._WINDOW_ICON
def get_home_path(self) -> str: return self._USER_HOME
def get_starting_files(self) -> list: return self._starting_files
def get_guake_key(self) -> tuple: return self._guake_key
def get_starting_args(self):
return self.args, self.unknownargs

View File

@@ -1,6 +1,7 @@
{
"keybindings": {
"help" : "F1",
"guake_key" : "",
"rename-files" : ["F2", "<Control>e"],
"open-terminal" : "F4",
"refresh-tab" : ["F5", "<Control>r"],

View File

@@ -6,6 +6,7 @@ popover > box,
.body-container,
.center-container,
.header-container,
.footer-container,
.left-containerm,
.right-container {
background: rgba(0, 0, 0, 0.0);