From 9248fc93401c171c587ff907592701b0c93f3d17 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Mon, 18 Sep 2023 21:27:32 -0500 Subject: [PATCH] Re-adding guake like functionality comit after bungled merge --- doc/terminator.1 | 24 +++++++ terminatorlib/optionparse.py | 13 +++- terminatorlib/window.py | 124 +++++++++++++++++++++++++++-------- 3 files changed, 132 insertions(+), 29 deletions(-) diff --git a/doc/terminator.1 b/doc/terminator.1 index b237030a..16ba5d48 100644 --- a/doc/terminator.1 +++ b/doc/terminator.1 @@ -90,6 +90,30 @@ Specify the preferred size and position of Terminator\(cqs window; see \fBX\fP(7). .RE .sp +\fB\-\-guake\-key\fP=\fIGUAKE_KEY\fP +.RS 4 +A Guake like terminal mode where terminal can be toggled by a key. +Usage Example: terminator \-\-guake\-key="F8" \-\-guake\-side left \-\-guake\-width 700 \-\-guake\-height 800 +.RE +.sp +\fB\-\-guake\-side\fP=\fIGUAKE_SIDE\fP +.RS 4 +Set the preferred screen edge position of a terminal in Guake like mode. +Options: top, bottom, left, right +Default: top +.RE +.sp +\fB\-\-guake\-width\fP=\fIGUAKE_WIDTH\fP +.RS 4 +Set the preferred width when using Guake like mode. +Default: 800 +.RE +.sp +\fB\-\-guake\-height\fP=\fIGUAKE_HEIGHT\fP +.RS 4 +Set the preferred height when using Guake like mode. +Default: 600 +.sp \fB\-e\fP \fICOMMAND\fP, \fB\-\-command\fP=\fICOMMAND\fP .RS 4 Run the specified command instead of the default shell or profile diff --git a/terminatorlib/optionparse.py b/terminatorlib/optionparse.py index cd20ff8b..2037e985 100644 --- a/terminatorlib/optionparse.py +++ b/terminatorlib/optionparse.py @@ -16,6 +16,7 @@ """Terminator.optionparse - Parse commandline options""" import argparse +from argparse import RawTextHelpFormatter import sys import os @@ -37,7 +38,7 @@ def parse_options(): """Parse the command line options""" is_x_terminal_emulator = os.path.basename(sys.argv[0]) == 'x-terminal-emulator' - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter) parser.add_argument('-R', '--reload', action='store_true', dest='reload', help=_('Reload terminator configuration')) @@ -59,6 +60,16 @@ def parse_options(): parser.add_argument('--geometry', dest='geometry', type=str, help=_('Set the preferred size and position of the window' '(see X man page)')) + parser.add_argument('--guake-key', dest='guake_key', type=str, + help=_('A Guake like terminal mode where terminal can be toggled by a key.\nUsage Example: terminator --guake-key="F8" --guake-side left --guake-width 700 --guake-height 800')) + parser.add_argument('--guake-side', dest='guake_side', default="top", type=str, + help=_('Set the preferred screen edge position of a terminal in Guake like mode.\nOptions: top, bottom, left, right\nDefault: top')) + parser.add_argument('--guake-width', dest='guake_width', default="800", type=str, + help=_('Set the preferred width when using Guake like mode. Default: 800' + '(see X man page)')) + parser.add_argument('--guake-height', dest='guake_height', default="600", type=str, + help=_('Set the preferred height when using Guake like mode. Default: 600' + '(see X man page)')) if not is_x_terminal_emulator: parser.add_argument('-e', '--command', dest='command', help=_('Specify a command to execute inside the terminal')) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 9cf7f688..2058119a 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -77,13 +77,16 @@ class Window(Container, Gtk.Window): # self.set_property('allow-shrink', True) # FIXME FOR GTK3, or do we need this actually? icon_to_apply='' - self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() - - self.preventHide = False + + self.preventHide = False + + self.display = Gdk.Display().get_default() + self.mouse = self.display.get_default_seat().get_pointer() + self.guake_key = None options = self.config.options_get() if options: @@ -92,15 +95,44 @@ class Window(Container, Gtk.Window): if options.role: self.set_role(options.role) - + if options.forcedicon is not None: icon_to_apply = options.forcedicon if options.geometry: if not self.parse_geometry(options.geometry): - err('Window::__init__: Unable to parse geometry: %s' % + err('Window::__init__: Unable to parse geometry: %s' % options.geometry) + if options.guake_key: + self.guake_key = options.guake_key + + if options.guake_side and options.guake_width and options.guake_height: + proceed_undecorated = True + + if options.guake_side in ["top", "bottom", "left", "right"]: + self.guake_side = options.guake_side + else: + proceed_undecorated = False + err('Window::__init__: Unable to parse guake_side: %s' % + options.guake_side) + + try: + self.guake_width = int(options.guake_width) + self.guake_height = int(options.guake_height) + except Exception as e: + proceed_undecorated = False + err('Window::__init__: Unable to parse guake_width: %s and/or guake_height: %s' % + options.guake_width, options.guake_height) + + if proceed_undecorated: + self.set_decorated(False) + self.set_default_size(self.guake_width, self.guake_height) + else: + self.guake_key = None + + self.register_callbacks() + self.apply_icon(icon_to_apply) self.pending_set_rough_geometry_hint = False self.hidefunc = self.hide @@ -119,6 +151,35 @@ class Window(Container, Gtk.Window): else: raise AttributeError('unknown property %s' % prop.name) + # NOTE: Gdk.VisibilityState.UNOBSCURED presumably isn't reliable due to transparency in moddern wms. + # Seems to work okay for our needs but should be kept in mind for future changes.... + def _bind_window_to_position(self, widget, eve): + if eve.state == Gdk.VisibilityState.UNOBSCURED: + screen, mouse_x, mouse_y = self.mouse.get_position() + monitor = self.display.get_monitor_at_point(mouse_x, mouse_y) + window_w, window_h = self.guake_width, self.guake_height + geom_rect = monitor.get_geometry() + x, y, w, h = geom_rect.x, geom_rect.y, geom_rect.width, geom_rect.height + + if not self.is_maximized() and not self.isfullscreen: + if self.guake_side == "top": + new_x = (w - (window_w + ((w - window_w)/2) )) + x + new_y = y + if self.guake_side == "bottom": + new_x = (w - (window_w + ((w - window_w)/2) )) + x + new_y = (h - window_h) + y + if self.guake_side == "left": + new_x = x + new_y = (h - (window_h + ((h - window_h)/2) )) + y + if self.guake_side == "right": + new_x = (w - window_w) + x + new_y = (h - (window_h + ((h - window_h)/2) )) + y + else: + new_x = x + new_y = y + + self.move(new_x, new_y) + def register_callbacks(self): """Connect the GTK+ signals we care about""" self.connect('key-press-event', self.on_key_press) @@ -129,22 +190,29 @@ class Window(Container, Gtk.Window): self.connect('focus-out-event', self.on_focus_out) self.connect('focus-in-event', self.on_focus_in) + if self.guake_key not in ('', None): + guake_unload_id = self.connect('visibility-notify-event', self._bind_window_to_position) + + _hide_key = self.config['keybindings']['hide_window'] + toggle_key = self.guake_key if self.guake_key not in ('', None) else _hide_key if _hide_key not in ('', None) else None + # Attempt to grab a global hotkey for hiding the window. # If we fail, we'll never hide the window, iconifying instead. - if self.config['keybindings']['hide_window'] not in ('', None): - if display_manager() == 'X11': - try: - self.hidebound = Keybinder.bind( - self.config['keybindings']['hide_window'], - self.on_hide_window) - except (KeyError, NameError): - pass + if toggle_key and display_manager() == 'X11': + try: + self.hidebound = Keybinder.bind(toggle_key, self.on_hide_window) + except (KeyError, NameError): + ... - if not self.hidebound: - err('Unable to bind hide_window key, another instance/window has it.') - self.hidefunc = self.iconify - else: - self.hidefunc = self.hide + if not self.hidebound: + err('Unable to bind hide_window key, another instance/window has it.') + self.hidefunc = self.iconify + + if self.guake_key not in ('', None): + GObject.signal_handler_disconnect(self, guake_unload_id) + self.set_decorated(True) + else: + self.hidefunc = self.hide def apply_config(self): """Apply various configuration options""" @@ -294,7 +362,7 @@ class Window(Container, Gtk.Window): def confirm_close(self, window, type): """Display a confirmation dialog when the user is closing multiple terminals in one window""" - + return(not (self.construct_confirm_close(window, type) == Gtk.ResponseType.ACCEPT)) def on_destroy_event(self, widget, data=None): @@ -342,7 +410,7 @@ class Window(Container, Gtk.Window): # pylint: disable-msg=W0613 def on_window_state_changed(self, window, event): """Handle the state of the window changing""" - self.isfullscreen = bool(event.new_window_state & + self.isfullscreen = bool(event.new_window_state & Gdk.WindowState.FULLSCREEN) self.ismaximised = bool(event.new_window_state & Gdk.WindowState.MAXIMIZED) @@ -401,7 +469,7 @@ class Window(Container, Gtk.Window): visual = screen.get_rgba_visual() if visual: self.set_visual(visual) - + def show(self, startup=False): """Undo the startup show request if started in hidden mode""" #Present is necessary to grab focus when window is hidden from taskbar. @@ -491,7 +559,7 @@ class Window(Container, Gtk.Window): container = maker.make('VPaned') else: container = maker.make('HPaned') - + self.set_pos_by_ratio = True if not sibling: @@ -515,7 +583,7 @@ class Window(Container, Gtk.Window): for term in order: container.add(term) container.show_all() - + while Gtk.events_pending(): Gtk.main_iteration_do(False) sibling.grab_focus() @@ -563,7 +631,7 @@ class Window(Container, Gtk.Window): self.set_property('term_zoomed', True) if font_scale: - widget.cnxids.new(widget, 'size-allocate', + widget.cnxids.new(widget, 'size-allocate', widget.zoom_scale, self.zoom_data) widget.grab_focus() @@ -636,7 +704,7 @@ class Window(Container, Gtk.Window): def get_terminals(self): return(util.enumerate_descendants(self)[1]) - + def get_visible_terminals(self): """Walk down the widget tree to find all of the visible terminals. Mostly using Container::get_visible_terminals()""" @@ -726,7 +794,7 @@ class Window(Container, Gtk.Window): extra_height = win_height - total_font_height dbg('setting geometry hints: (ewidth:%s)(eheight:%s),\ -(fwidth:%s)(fheight:%s)' % (extra_width, extra_height, +(fwidth:%s)(fheight:%s)' % (extra_width, extra_height, font_width, font_height)) geometry = Gdk.Geometry() geometry.base_width = extra_width @@ -850,7 +918,7 @@ class Window(Container, Gtk.Window): if not maker.isinstance(notebook, 'Notebook'): dbg('note in a notebook, refusing to ungroup tab') return - + self.set_groups(None, self.get_visible_terminals()) def move_tab(self, widget, direction): @@ -883,7 +951,7 @@ class Window(Container, Gtk.Window): else: err('unknown direction: %s' % direction) return - + notebook.reorder_child(child, page) def navigate_terminal(self, terminal, direction):