Compare commits
9 Commits
cca007db76
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 530fe7c3ab | |||
| 5e5cc50ba8 | |||
| 22736147e6 | |||
| 4c25ce297c | |||
| 60b55da973 | |||
| 7c0d87fd20 | |||
| 4cd5a1f089 | |||
| 18dc71dcb0 | |||
| dd3e87f636 |
@@ -4,4 +4,4 @@ setproctitle==1.2.2
|
||||
pyxdg==0.27
|
||||
psutil==5.8.0
|
||||
pycryptodome==3.20.0
|
||||
sqlmodel==0.0.19
|
||||
sqlmodel==0.0.19
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Python imports
|
||||
import builtins
|
||||
import traceback
|
||||
import threading
|
||||
import sys
|
||||
|
||||
@@ -31,6 +32,17 @@ def daemon_threaded_wrapper(fn):
|
||||
return thread
|
||||
return wrapper
|
||||
|
||||
def call_chain_wrapper(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
print()
|
||||
print()
|
||||
for line in traceback.format_stack():
|
||||
print( line.strip() )
|
||||
print()
|
||||
print()
|
||||
|
||||
return fn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
# NOTE: Just reminding myself we can add to builtins two different ways...
|
||||
@@ -54,6 +66,7 @@ builtins.logger = Logger(
|
||||
|
||||
builtins.threaded = threaded_wrapper
|
||||
builtins.daemon_threaded = daemon_threaded_wrapper
|
||||
builtins.call_chain = call_chain_wrapper
|
||||
|
||||
|
||||
|
||||
@@ -64,4 +77,4 @@ def custom_except_hook(exc_type, exc_value, exc_traceback):
|
||||
|
||||
logger.error("Uncaught exception", exc_info = (exc_type, exc_value, exc_traceback))
|
||||
|
||||
sys.excepthook = custom_except_hook
|
||||
sys.excepthook = custom_except_hook
|
||||
|
||||
@@ -28,7 +28,9 @@ def main():
|
||||
settings_manager.set_trace_debug(True)
|
||||
|
||||
settings_manager.do_dirty_start_check()
|
||||
Application()
|
||||
|
||||
app = Application()
|
||||
app.run()
|
||||
|
||||
|
||||
|
||||
|
||||
17
src/app.py
17
src/app.py
@@ -1,4 +1,5 @@
|
||||
# Python imports
|
||||
from contextlib import suppress
|
||||
import signal
|
||||
import os
|
||||
|
||||
@@ -26,9 +27,12 @@ class Application:
|
||||
self.load_ipc()
|
||||
|
||||
self.setup_debug_hook()
|
||||
Window().main()
|
||||
|
||||
|
||||
def run(self):
|
||||
win = Window()
|
||||
win.start()
|
||||
|
||||
def load_ipc(self):
|
||||
args, \
|
||||
unknownargs = settings_manager.get_starting_args()
|
||||
@@ -49,18 +53,15 @@ class Application:
|
||||
except Exception:
|
||||
ipc_server.send_test_ipc_message()
|
||||
|
||||
try:
|
||||
with suppress(Exception):
|
||||
ipc_server.create_ipc_listener()
|
||||
except Exception as e:
|
||||
...
|
||||
|
||||
def setup_debug_hook(self):
|
||||
try:
|
||||
# Typically: ValueError: signal only works in main thread
|
||||
with suppress(ValueError):
|
||||
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
|
||||
signal.signal(
|
||||
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
|
||||
debug_signal_handler
|
||||
)
|
||||
except ValueError:
|
||||
# Typically: ValueError: signal only works in main thread
|
||||
...
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
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._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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ from .bridge_controller import BridgeController
|
||||
|
||||
|
||||
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
|
||||
""" docstring for BaseController. """
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._setup_controller_data()
|
||||
@@ -52,8 +54,6 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
|
||||
args, unknownargs = settings_manager.get_starting_args()
|
||||
if args.no_plugins == "false":
|
||||
self.plugins_controller.pre_launch_plugins()
|
||||
|
||||
if args.no_plugins == "false":
|
||||
self.plugins_controller.post_launch_plugins()
|
||||
|
||||
for file in settings_manager.get_starting_files():
|
||||
|
||||
@@ -37,14 +37,19 @@ class BaseControllerData:
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
if os.path.isdir( arg.replace("file://", "") ):
|
||||
files.append( f"DIR|{arg.replace('file://', '')}" )
|
||||
continue
|
||||
|
||||
# NOTE: If passing line number with file split against :
|
||||
if os.path.isfile( arg.replace("file://", "").split(":")[0] ):
|
||||
files.append( f"FILE|{arg.replace('file://', '')}" )
|
||||
continue
|
||||
|
||||
if len(files) > 0:
|
||||
settings_manager.set_is_starting_with_file(True)
|
||||
settings_manager.set_starting_files(files)
|
||||
logger.info(f"Not a File: {arg}")
|
||||
|
||||
if len(files) == 0: return
|
||||
|
||||
settings_manager.set_is_starting_with_file(True)
|
||||
settings_manager.set_starting_files(files)
|
||||
|
||||
def get_base_container(self):
|
||||
return self.base_container
|
||||
@@ -80,25 +85,23 @@ class BaseControllerData:
|
||||
widget.remove(child)
|
||||
|
||||
def get_clipboard_data(self, encoding = "utf-8") -> str:
|
||||
if which("xclip"):
|
||||
command = ['xclip','-selection','clipboard']
|
||||
else:
|
||||
if not which("xclip"):
|
||||
logger.info('xclip not found...')
|
||||
return
|
||||
|
||||
command = ['xclip','-selection','clipboard']
|
||||
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout = subprocess.PIPE)
|
||||
retcode = proc.wait()
|
||||
data = proc.stdout.read()
|
||||
return data.decode(encoding).strip()
|
||||
|
||||
def set_clipboard_data(self, data: type, encoding = "utf-8") -> None:
|
||||
if which("xclip"):
|
||||
command = ['xclip','-selection','clipboard']
|
||||
else:
|
||||
if not which("xclip"):
|
||||
logger.info('xclip not found...')
|
||||
return
|
||||
|
||||
command = ['xclip','-selection','clipboard']
|
||||
proc = subprocess.Popen(command, stdin = subprocess.PIPE)
|
||||
proc.stdin.write(data.encode(encoding))
|
||||
proc.stdin.close()
|
||||
retcode = proc.wait()
|
||||
retcode = proc.wait()
|
||||
|
||||
@@ -10,8 +10,6 @@ import base64
|
||||
class BridgeController:
|
||||
def __init__(self):
|
||||
|
||||
self.opened_files = {}
|
||||
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# Python imports
|
||||
from contextlib import suppress
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gio
|
||||
|
||||
# Application imports
|
||||
@@ -60,11 +59,9 @@ class OpenFilesButton(Gtk.Button):
|
||||
|
||||
chooser.set_select_multiple(True)
|
||||
|
||||
try:
|
||||
with suppress(Exception):
|
||||
folder = widget.get_current_file().get_parent() if not start_dir else start_dir
|
||||
chooser.set_current_folder( folder.get_path() )
|
||||
except Exception as e:
|
||||
...
|
||||
|
||||
response = chooser.run()
|
||||
if not response == Gtk.ResponseType.OK:
|
||||
|
||||
72
src/core/widgets/controls/save_as_button.py
Normal file
72
src/core/widgets/controls/save_as_button.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# Python imports
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gio
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class SaveAsButton(Gtk.Button):
|
||||
def __init__(self):
|
||||
super(SaveAsButton, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_label("Save As")
|
||||
self.set_image( Gtk.Image.new_from_icon_name("gtk-save-as", 4) )
|
||||
self.set_always_show_image(True)
|
||||
self.set_image_position(1) # Left - 0, Right = 1
|
||||
self.set_hexpand(False)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("released", self._save_as)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("save-as", self._save_as)
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
def _save_as(self, widget = None, eve = None, gfile = None):
|
||||
start_dir = None
|
||||
_gfile = None
|
||||
|
||||
chooser = Gtk.FileChooserDialog("Save File As...", None,
|
||||
Gtk.FileChooserAction.SAVE,
|
||||
(
|
||||
Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_SAVE_AS,
|
||||
Gtk.ResponseType.OK
|
||||
)
|
||||
)
|
||||
|
||||
# chooser.set_select_multiple(False)
|
||||
|
||||
response = chooser.run()
|
||||
if not response == Gtk.ResponseType.OK:
|
||||
chooser.destroy()
|
||||
return _gfile
|
||||
|
||||
file = chooser.get_filename()
|
||||
if not file:
|
||||
chooser.destroy()
|
||||
return _gfile
|
||||
|
||||
path = file if os.path.isabs(file) else os.path.abspath(file)
|
||||
_gfile = Gio.File.new_for_path(path)
|
||||
|
||||
chooser.destroy()
|
||||
|
||||
logger.debug(f"File To Save As: {_gfile}")
|
||||
return _gfile
|
||||
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)
|
||||
@@ -93,13 +93,16 @@ class VteWidget(Vte.Terminal):
|
||||
if not text.encode() == "\r".encode(): return
|
||||
|
||||
text, attributes = self.get_text()
|
||||
|
||||
if not text: return
|
||||
|
||||
lines = text.strip().splitlines()
|
||||
command_ran = None
|
||||
|
||||
try:
|
||||
command_ran = lines[-1].split("-->:")[1].strip()
|
||||
except VteWidgetException as e:
|
||||
logger.debud(e)
|
||||
logger.debug(e)
|
||||
return
|
||||
|
||||
if not command_ran[0:3].encode() in self.cd_cmd_prefix:
|
||||
@@ -114,12 +117,12 @@ class VteWidget(Vte.Terminal):
|
||||
event = Event("pty_path_updated", "", target_path)
|
||||
event_system.emit("handle_bridge_event", (event,))
|
||||
|
||||
def update_term_path(self, fpath):
|
||||
def update_term_path(self, fpath: str):
|
||||
self.dont_process = True
|
||||
|
||||
cmds = [f"cd '{fpath}'\n", "clear\n"]
|
||||
for i in cmds:
|
||||
self.run_command(i)
|
||||
for cmd in cmds:
|
||||
self.run_command(cmd)
|
||||
|
||||
def run_command(self, cmd):
|
||||
def run_command(self, cmd: str):
|
||||
self.feed_child_binary(bytes(cmd, 'utf8'))
|
||||
@@ -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
|
||||
|
||||
@@ -56,11 +68,11 @@ class Window(Gtk.ApplicationWindow):
|
||||
self.connect("focus-in-event", self._on_focus_in_event)
|
||||
self.connect("focus-out-event", self._on_focus_out_event)
|
||||
|
||||
self.connect("delete-event", self._tear_down)
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down)
|
||||
self.connect("delete-event", self.stop)
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.stop)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("tear-down", self._tear_down)
|
||||
event_system.subscribe("tear-down", self.stop)
|
||||
event_system.subscribe("load-interactive-debug", self._load_interactive_debug)
|
||||
|
||||
def _load_widgets(self):
|
||||
@@ -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,8 +139,53 @@ 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
|
||||
|
||||
def _tear_down(self, widget = None, eve = None):
|
||||
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()
|
||||
|
||||
def stop(self, widget = None, eve = None):
|
||||
event_system.emit("shutting-down")
|
||||
|
||||
size = self.get_size()
|
||||
@@ -133,6 +199,3 @@ class Window(Gtk.ApplicationWindow):
|
||||
|
||||
settings_manager.clear_pid()
|
||||
Gtk.main_quit()
|
||||
|
||||
def main(self):
|
||||
Gtk.main()
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,13 +12,11 @@ class SingletonError(Exception):
|
||||
|
||||
|
||||
class Singleton:
|
||||
ccount = 0
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = super(Singleton, cls).__new__(cls)
|
||||
cls.ccount += 1
|
||||
if cls._instance:
|
||||
raise SingletonError(f"'{cls.__name__}' is a Singleton. Cannot create a new instance...")
|
||||
|
||||
if cls.ccount == 2:
|
||||
raise SingletonError(f"Exceeded {cls.__name__} instantiation limit...")
|
||||
|
||||
return obj
|
||||
cls._instance = super(Singleton, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"keybindings": {
|
||||
"help" : "F1",
|
||||
"guake_key" : "",
|
||||
"rename-files" : ["F2", "<Control>e"],
|
||||
"open-terminal" : "F4",
|
||||
"refresh-tab" : ["F5", "<Control>r"],
|
||||
"delete-files" : "Delete",
|
||||
"load-interactive-debug" : "<Control>d",
|
||||
"tggl-top-main-menubar" : "Alt_L",
|
||||
"tggl-top-main-menubar" : "<Control>0",
|
||||
"trash-files" : "<Shift><Control>t",
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
/* ---- Make most desired things base transparent ---- */
|
||||
/* ---- Make most desired things base transparent ---- */
|
||||
popover,
|
||||
popover > box
|
||||
popover > box,
|
||||
notebook,
|
||||
header,
|
||||
stack,
|
||||
scrolledwindow,
|
||||
viewport {
|
||||
background: rgba(0, 0, 0, 0.0);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
tab {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
tab:checked {
|
||||
border-bottom-color: rgba(125, 125, 125, 1);
|
||||
}
|
||||
|
||||
|
||||
.main-window,
|
||||
.base-container,
|
||||
.body-container,
|
||||
.center-container,
|
||||
.header-container,
|
||||
.footer-container,
|
||||
.left-containerm,
|
||||
.right-container {
|
||||
background: rgba(0, 0, 0, 0.0);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
|
||||
.base-container {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user