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:
@@ -8,6 +8,7 @@ from gi.repository import Gtk
|
|||||||
# Application imports
|
# Application imports
|
||||||
from .header_container import HeaderContainer
|
from .header_container import HeaderContainer
|
||||||
from .body_container import BodyContainer
|
from .body_container import BodyContainer
|
||||||
|
from .footer_container import FooterContainer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ class BaseContainer(Gtk.Box):
|
|||||||
def _load_widgets(self):
|
def _load_widgets(self):
|
||||||
self.add(HeaderContainer())
|
self.add(HeaderContainer())
|
||||||
self.add(BodyContainer())
|
self.add(BodyContainer())
|
||||||
|
self.add(FooterContainer())
|
||||||
|
|
||||||
def _update_transparency(self):
|
def _update_transparency(self):
|
||||||
self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}")
|
self.ctx.add_class(f"mw_transparency_{settings.theming.transparency}")
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ class CenterContainer(Gtk.Box):
|
|||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
self.set_hexpand(True)
|
||||||
|
self.set_vexpand(True)
|
||||||
|
|
||||||
ctx = self.get_style_context()
|
ctx = self.get_style_context()
|
||||||
ctx.add_class("center-container")
|
ctx.add_class("center-container")
|
||||||
|
|
||||||
|
|||||||
41
src/core/containers/footer_container.py
Normal file
41
src/core/containers/footer_container.py
Normal 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):
|
||||||
|
...
|
||||||
@@ -22,11 +22,14 @@ class HeaderContainer(Gtk.Box):
|
|||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
self._load_widgets()
|
self._load_widgets()
|
||||||
|
|
||||||
self.show_all()
|
self.show()
|
||||||
|
|
||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_orientation(Gtk.Orientation.HORIZONTAL)
|
self.set_orientation(Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
|
self.set_hexpand(True)
|
||||||
|
|
||||||
self.ctx.add_class("header-container")
|
self.ctx.add_class("header-container")
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class LeftContainer(Gtk.Box):
|
|||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
self.set_vexpand(True)
|
||||||
|
|
||||||
ctx = self.get_style_context()
|
ctx = self.get_style_context()
|
||||||
ctx.add_class("left-container")
|
ctx.add_class("left-container")
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ class RightContainer(Gtk.Box):
|
|||||||
|
|
||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
self.set_vexpand(True)
|
||||||
|
|
||||||
ctx = self.get_style_context()
|
ctx = self.get_style_context()
|
||||||
ctx.add_class("right-container")
|
ctx.add_class("right-container")
|
||||||
|
|
||||||
|
|||||||
@@ -104,4 +104,4 @@ class BaseControllerData:
|
|||||||
proc = subprocess.Popen(command, stdin = subprocess.PIPE)
|
proc = subprocess.Popen(command, stdin = subprocess.PIPE)
|
||||||
proc.stdin.write(data.encode(encoding))
|
proc.stdin.write(data.encode(encoding))
|
||||||
proc.stdin.close()
|
proc.stdin.close()
|
||||||
retcode = proc.wait()
|
retcode = proc.wait()
|
||||||
|
|||||||
110
src/core/widgets/clock_widget.py
Normal file
110
src/core/widgets/clock_widget.py
Normal 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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
35
src/core/widgets/pager_widget.py
Normal file
35
src/core/widgets/pager_widget.py
Normal 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()
|
||||||
54
src/core/widgets/task_list_widget.py
Normal file
54
src/core/widgets/task_list_widget.py
Normal 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)
|
||||||
@@ -10,6 +10,11 @@ from gi.repository import Gtk
|
|||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
|
||||||
|
try:
|
||||||
|
from gi.repository import GdkX11
|
||||||
|
except ImportError:
|
||||||
|
logger.debug("Could not import X11 gir module...")
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from libs.status_icon import StatusIcon
|
from libs.status_icon import StatusIcon
|
||||||
from core.controllers.base_controller import BaseController
|
from core.controllers.base_controller import BaseController
|
||||||
@@ -31,6 +36,9 @@ class Window(Gtk.ApplicationWindow):
|
|||||||
self._status_icon = None
|
self._status_icon = None
|
||||||
self._controller = None
|
self._controller = None
|
||||||
|
|
||||||
|
self.guake_key = settings_manager.get_guake_key()
|
||||||
|
self.hidefunc = None
|
||||||
|
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
self._setup_signals()
|
self._setup_signals()
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
@@ -38,6 +46,7 @@ class Window(Gtk.ApplicationWindow):
|
|||||||
|
|
||||||
self._set_window_data()
|
self._set_window_data()
|
||||||
self._set_size_constraints()
|
self._set_size_constraints()
|
||||||
|
self._setup_window_toggle_event()
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
@@ -45,6 +54,9 @@ class Window(Gtk.ApplicationWindow):
|
|||||||
def _setup_styling(self):
|
def _setup_styling(self):
|
||||||
self.set_title(f"{APP_NAME}")
|
self.set_title(f"{APP_NAME}")
|
||||||
self.set_icon_from_file( settings_manager.get_window_icon() )
|
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_gravity(5) # 5 = CENTER
|
||||||
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
|
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
|
||||||
|
|
||||||
@@ -74,6 +86,15 @@ class Window(Gtk.ApplicationWindow):
|
|||||||
|
|
||||||
self.add( self._controller.get_base_container() )
|
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):
|
def _set_size_constraints(self):
|
||||||
_window_x = settings.config.main_window_x
|
_window_x = settings.config.main_window_x
|
||||||
_window_y = settings.config.main_window_y
|
_window_y = settings.config.main_window_y
|
||||||
@@ -118,6 +139,48 @@ class Window(Gtk.ApplicationWindow):
|
|||||||
def _load_interactive_debug(self):
|
def _load_interactive_debug(self):
|
||||||
self.set_interactive_debugging(True)
|
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):
|
def start(self):
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class IPCServer(Singleton):
|
|||||||
""" Create a listener so that other {APP_NAME} instances send requests back to existing instance. """
|
""" 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"):
|
def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"):
|
||||||
self.is_ipc_alive = False
|
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._ipc_address = ipc_address
|
||||||
self._conn_type = conn_type
|
self._conn_type = conn_type
|
||||||
self._ipc_authkey = b'' + bytes(f'{APP_NAME}-ipc', 'utf-8')
|
self._ipc_authkey = b'' + bytes(f'{APP_NAME}-ipc', 'utf-8')
|
||||||
|
|||||||
@@ -91,7 +91,9 @@ class SettingsManager(StartCheckMixin, Singleton):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self._KEY_BINDINGS_FILE) as file:
|
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)
|
keybindings.configure(bindings)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print( f"Settings Manager: {self._KEY_BINDINGS_FILE}\n\t\t{repr(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_window_icon(self) -> str: return self._WINDOW_ICON
|
||||||
def get_home_path(self) -> str: return self._USER_HOME
|
def get_home_path(self) -> str: return self._USER_HOME
|
||||||
def get_starting_files(self) -> list: return self._starting_files
|
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):
|
def get_starting_args(self):
|
||||||
return self.args, self.unknownargs
|
return self.args, self.unknownargs
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"keybindings": {
|
"keybindings": {
|
||||||
"help" : "F1",
|
"help" : "F1",
|
||||||
|
"guake_key" : "",
|
||||||
"rename-files" : ["F2", "<Control>e"],
|
"rename-files" : ["F2", "<Control>e"],
|
||||||
"open-terminal" : "F4",
|
"open-terminal" : "F4",
|
||||||
"refresh-tab" : ["F5", "<Control>r"],
|
"refresh-tab" : ["F5", "<Control>r"],
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ popover > box,
|
|||||||
.body-container,
|
.body-container,
|
||||||
.center-container,
|
.center-container,
|
||||||
.header-container,
|
.header-container,
|
||||||
|
.footer-container,
|
||||||
.left-containerm,
|
.left-containerm,
|
||||||
.right-container {
|
.right-container {
|
||||||
background: rgba(0, 0, 0, 0.0);
|
background: rgba(0, 0, 0, 0.0);
|
||||||
|
|||||||
Reference in New Issue
Block a user