Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 91 additions & 4 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,95 @@ tomli = "*"
pre-commit = "==2.17.0"

[packages]
pbr = "*"
wheel = "*"
typing = {markers = "python_version < '3.5'"}
cython = "*"
pyyaml = "*"
alabaster = "==1.0.0"
astroid = "==3.3.8"
autopep8 = "==2.3.1"
babel = "==2.16.0"
black = "==24.10.0"
certifi = "==2024.12.14"
cffi = "==1.17.1"
cfgv = "==3.4.0"
charset-normalizer = "==3.4.1"
click = "==8.1.8"
colorlog = "==6.9.0"
coverage = {extras = ["toml"], version = "==7.6.10"}
cryptography = "==44.0.0"
dill = "==0.3.9"
distlib = "==0.3.9"
docutils = "==0.21.2"
dulwich = "==0.22.7"
filelock = "==3.16.1"
fiximports = "==0.1.18"
flake8 = "==7.1.1"
identify = "==2.6.4"
idna = "==3.10"
imagesize = "==1.4.1"
iniconfig = "==2.0.0"
isort = "==5.13.2"
"jaraco.classes" = "==3.4.0"
"jaraco.context" = "==6.0.1"
"jaraco.functools" = "==4.1.0"
jeepney = "==0.8.0"
jinja2 = "==3.1.5"
keyring = "==25.6.0"
markdown-it-py = "==3.0.0"
markupsafe = "==3.0.2"
mccabe = "==0.7.0"
mdurl = "==0.1.2"
mock = "==5.1.0"
more-itertools = "==10.5.0"
mypy-extensions = "==1.0.0"
nh3 = "==0.2.20"
nodeenv = "==1.9.1"
packaging = "==24.2"
pathlib2 = "==2.3.7.post1"
pathspec = "==0.12.1"
pbr = "==6.1.0"
pep8 = "==1.7.1"
pew = "==1.2.0"
pkginfo = "==1.12.0"
platformdirs = "==4.3.6"
pluggy = "==1.5.0"
pre-commit = "==2.17.0"
pycodestyle = "==2.12.1"
pycparser = "==2.22"
pyfakefs = "==5.7.3"
pyflakes = "==3.2.0"
pygments = "==2.18.0"
pylint = "==3.3.2"
pytest = "==8.3.4"
pytest-cov = "==6.0.0"
pytest-mock = "==3.14.0"
pyyaml = "==6.0.2"
readme-renderer = "==44.0"
reno = {extras = ["sphinx"], version = "==4.1.0"}
requests = "==2.32.3"
requests-toolbelt = "==1.0.0"
rfc3986 = "==2.0.0"
rich = "==13.9.4"
scandir = "==1.10.0"
secretstorage = "==3.3.3"
six = "==1.17.0"
snowballstemmer = "==2.2.0"
sphinx = "==8.1.3"
sphinx-rtd-theme = "==3.0.2"
sphinxcontrib-applehelp = "==2.0.0"
sphinxcontrib-devhelp = "==2.0.0"
sphinxcontrib-htmlhelp = "==2.1.0"
sphinxcontrib-jquery = "==4.1"
sphinxcontrib-jsmath = "==1.0.1"
sphinxcontrib-programoutput = "==0.18"
sphinxcontrib-qthelp = "==2.0.0"
sphinxcontrib-serializinghtml = "==2.0.0"
toml = "==0.10.2"
tomli = "==2.2.1"
tomlkit = "==0.13.2"
twine = "==6.0.1"
typing-extensions = "==4.12.2"
urllib3 = "==2.3.0"
virtualenv = "==20.28.0"
virtualenv-clone = "==0.5.7"
yapf = "==0.43.0"
cython = "==3.0.11"
typing = "==3.7.4.3"
289 changes: 231 additions & 58 deletions Pipfile.lock

Large diffs are not rendered by default.

38 changes: 36 additions & 2 deletions guake/boxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

gi.require_version("Vte", "2.91") # vte-0.42
gi.require_version("Gtk", "3.0")
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gio
Expand Down Expand Up @@ -636,10 +637,17 @@ def remove_dead_child(self, child):
print("I have never seen this widget!")


# Foreground color applied to a background tab's title when it has unseen
# activity. Chosen to stay readable on both light and dark tab bars.
TAB_ACTIVITY_COLOR = "#E8A33D"


class TabLabelEventBox(Gtk.EventBox):
def __init__(self, notebook, text, settings):
super().__init__()
self.notebook = notebook
self._text = text
self._activity = False
self.box = Gtk.Box(homogeneous=Gtk.Orientation.HORIZONTAL, spacing=0, visible=True)
self.label = Gtk.Label(label=text, visible=True)
self.close_button = Gtk.Button(
Expand All @@ -656,10 +664,36 @@ def __init__(self, notebook, text, settings):
self.connect("button-press-event", self.on_button_press, self.label)

def set_text(self, text):
self.label.set_text(text)
self._text = text
self._render()

def get_text(self):
return self.label.get_text()
return self._text

def set_activity(self, active):
"""Highlight (or clear) this tab's title to signal unseen output.

Returns True if the activity state actually changed, so callers can
avoid redundant re-rendering on the frequent contents-changed signal.
"""
active = bool(active)
if active == self._activity:
return False
self._activity = active
self._render()
return True

def get_activity(self):
return self._activity

def _render(self):
if self._activity:
self.label.set_markup(
f'<span foreground="{TAB_ACTIVITY_COLOR}" weight="bold">'
f"{GLib.markup_escape_text(self._text)}</span>"
)
else:
self.label.set_text(self._text)

def grab_focus_on_last_focused_terminal(self):
server_time = get_server_time(self.notebook.guake.window)
Expand Down
15 changes: 15 additions & 0 deletions guake/data/org.guake.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@
<summary>Audible bell</summary>
<description>If true, the system alert sound will be played on a bell character.</description>
</key>
<key name="display-tab-activity" type="b">
<default>false</default>
<summary>Highlight tabs with activity</summary>
<description>If true, the title of a background tab is highlighted when its terminal produces output, until the tab is selected.</description>
</key>
<key name="tab-activity-new-tab-grace" type="d">
<default>3.0</default>
<summary>Activity grace period for new tabs</summary>
<description>Number of seconds during which activity is ignored on a newly created tab, to avoid highlighting it because of its initial shell prompt.</description>
</key>
<key name="tab-activity-focus-loss-grace" type="d">
<default>1.0</default>
<summary>Activity grace period after losing focus</summary>
<description>Number of seconds during which activity is ignored on a tab right after it loses focus.</description>
</key>
<key name="window-width" type="i">
<default>100</default>
<summary>Window width.</summary>
Expand Down
110 changes: 110 additions & 0 deletions guake/data/prefs.glade
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkAdjustment" id="tab_activity_new_tab_grace_adj">
<property name="upper">60</property>
<property name="step-increment">0.5</property>
<property name="page-increment">5</property>
</object>
<object class="GtkAdjustment" id="tab_activity_focus_loss_grace_adj">
<property name="upper">60</property>
<property name="step-increment">0.5</property>
<property name="page-increment">5</property>
</object>
<object class="GtkAdjustment" id="scrollback_lines">
<property name="upper">10000000</property>
<property name="value">5000</property>
Expand Down Expand Up @@ -397,6 +407,106 @@
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="display_tab_activity">
<property name="label" translatable="yes">Highlight tabs with activity</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="halign">start</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
<signal name="toggled" handler="on_display_tab_activity_toggled" swapped="no"/>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox_tab_activity_new_tab_grace">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="lbl_tab_activity_new_tab_grace">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Ignore activity on new tabs for (seconds):</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="tab_activity_new_tab_grace">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="text" translatable="yes">3.0</property>
<property name="adjustment">tab_activity_new_tab_grace_adj</property>
<property name="climb-rate">1</property>
<property name="digits">1</property>
<property name="numeric">True</property>
<signal name="value-changed" handler="on_tab_activity_new_tab_grace_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox_tab_activity_focus_loss_grace">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="lbl_tab_activity_focus_loss_grace">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Ignore activity after losing focus for (seconds):</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="tab_activity_focus_loss_grace">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="text" translatable="yes">1.0</property>
<property name="adjustment">tab_activity_focus_loss_grace_adj</property>
<property name="climb-rate">1</property>
<property name="digits">1</property>
<property name="numeric">True</property>
<signal name="value-changed" handler="on_tab_activity_focus_loss_grace_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
Expand Down
9 changes: 9 additions & 0 deletions guake/gsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def __init__(self, guake_inst):
settings.general.onChangedValue("max-tab-name-length", self.max_tab_name_length_changed)
settings.general.onChangedValue("display-tab-names", self.display_tab_names_changed)
settings.general.onChangedValue("hide-tabs-if-one-tab", self.hide_tabs_if_one_tab_changed)
settings.general.onChangedValue("display-tab-activity", self.display_tab_activity_changed)

def custom_command_file_changed(self, settings, key, user_data):
self.guake.load_custom_commands()
Expand Down Expand Up @@ -475,3 +476,11 @@ def hide_tabs_if_one_tab_changed(self, settings, key, user_data):
be called and will show/hide the tab bar if necessary
"""
self.guake.get_notebook().hide_tabbar_if_one_tab()

def display_tab_activity_changed(self, settings, key, user_data):
"""If the gconf var display-tab-activity was disabled, clear any
activity highlights that are currently shown on tabs.
"""
if not settings.get_boolean(key):
for notebook in self.guake.notebook_manager.iter_notebooks():
notebook.clear_all_tab_activity()
Loading