diff --git a/nwg_panel/config.py b/nwg_panel/config.py
index d1240d99..775bf613 100644
--- a/nwg_panel/config.py
+++ b/nwg_panel/config.py
@@ -1747,7 +1747,9 @@ def edit_sway_workspaces(self, *args):
"mark-autotiling": True,
"mark-content": True,
"show-layout": True,
- "angle": 0.0
+ "angle": 0.0,
+ "hide-empty": False,
+ "hide-other-outputs": False,
}
for key in defaults:
check_key(settings, key, defaults[key])
@@ -1796,6 +1798,14 @@ def edit_sway_workspaces(self, *args):
self.ws_show_name.set_label(voc["show-window-name"])
self.ws_show_name.set_active(settings["show-name"])
+ self.ws_hide_empty = builder.get_object("hide-empty")
+ self.ws_hide_empty.set_label(voc["hide-empty"])
+ self.ws_hide_empty.set_active(settings["hide-empty"])
+
+ self.ws_hide_other_outputs = builder.get_object("hide-other-outputs")
+ self.ws_hide_other_outputs.set_label(voc["hide-other-outputs"])
+ self.ws_hide_other_outputs.set_active(settings["hide-other-outputs"])
+
self.ws_image_size = builder.get_object("image-size")
self.ws_image_size.set_numeric(True)
adj = Gtk.Adjustment(value=0, lower=8, upper=129, step_increment=1, page_increment=10, page_size=1)
@@ -1832,8 +1842,11 @@ def update_sway_workspaces(self):
settings = self.panel["sway-workspaces"]
val = self.eb_workspaces_menu.get_text()
- if val:
+ if val is not None:
+ print('numbers', repr(settings["numbers"]), repr(val.split()))
settings["numbers"] = val.split()
+ print('numbers', repr(settings["numbers"]), repr(val.split()))
+
val = self.ws_custom_labels.get_text()
settings["custom-labels"] = val.split()
@@ -1849,6 +1862,14 @@ def update_sway_workspaces(self):
if val is not None:
settings["show-name"] = val
+ val = self.ws_hide_other_outputs.get_active()
+ if val is not None:
+ settings["hide-other-outputs"] = val
+
+ val = self.ws_hide_empty.get_active()
+ if val is not None:
+ settings["hide-empty"] = val
+
settings["image-size"] = int(self.ws_image_size.get_value())
settings["name-length"] = int(self.ws_name_length.get_value())
@@ -2598,7 +2619,9 @@ def edit_executor(self, item, name, new=False):
"interval": 1,
"angle": 0.0,
"sigrt": signal.SIGRTMIN,
- "use-sigrt": False
+ "use-sigrt": False,
+ "icon": "view-refresh-symbolic",
+ "continuous": False
}
for key in defaults:
check_key(settings, key, defaults[key])
@@ -2660,6 +2683,9 @@ def edit_executor(self, item, name, new=False):
self.executor_icon_placement = builder.get_object("icon-placement")
self.executor_icon_placement.set_active_id(settings["icon-placement"])
+ self.executor_icon = builder.get_object("icon")
+ self.executor_icon.set_text(settings["icon"])
+
self.executor_icon_size = builder.get_object("icon-size")
self.executor_icon_size.set_numeric(True)
adj = Gtk.Adjustment(value=0, lower=8, upper=128, step_increment=1, page_increment=10, page_size=1)
@@ -2689,6 +2715,10 @@ def edit_executor(self, item, name, new=False):
self.executor_use_sigrt.set_label(voc["use-signal"])
self.executor_use_sigrt.set_active(settings["use-sigrt"])
+ self.executor_continuous = builder.get_object("continuous")
+ self.executor_continuous.set_label(voc["continuous"])
+ self.executor_continuous.set_active(settings["continuous"])
+
self.executor_remove = builder.get_object("remove")
self.executor_remove.set_label(voc["remove-executor"])
@@ -2736,6 +2766,7 @@ def update_executor(self):
val = self.executor_icon_placement.get_active_id()
if val:
settings["icon-placement"] = val
+ settings["icon"] = self.executor_icon.get_text()
settings["icon-size"] = int(self.executor_icon_size.get_value())
settings["interval"] = int(self.executor_interval.get_value())
@@ -2746,6 +2777,7 @@ def update_executor(self):
settings["sigrt"] = int(self.executor_sigrt.get_value())
settings["use-sigrt"] = self.executor_use_sigrt.get_active()
+ settings["continuous"] = self.executor_continuous.get_active()
self.panel[config_key] = settings
else:
diff --git a/nwg_panel/glade/config_executor.glade b/nwg_panel/glade/config_executor.glade
index 0c16187c..b3818718 100644
--- a/nwg_panel/glade/config_executor.glade
+++ b/nwg_panel/glade/config_executor.glade
@@ -205,6 +205,20 @@
2
+
+
+
+ 2
+ 2
+
+
+
+
+
+ 0
+ 16
+
+
+
+
+
+ 1
+ 16
+
+
1
- 16
+ 17
@@ -369,7 +406,7 @@ executor will create a new one.
0
- 16
+ 17
diff --git a/nwg_panel/glade/config_sway_workspaces.glade b/nwg_panel/glade/config_sway_workspaces.glade
index 97fca26c..9a9f29b4 100644
--- a/nwg_panel/glade/config_sway_workspaces.glade
+++ b/nwg_panel/glade/config_sway_workspaces.glade
@@ -226,7 +226,32 @@
-
+
+
+ 0
+ 10
+
+
+
+
+
+ 1
+ 10
+
diff --git a/nwg_panel/langs/en_US.json b/nwg_panel/langs/en_US.json
index e3ac33b3..9e947b49 100644
--- a/nwg_panel/langs/en_US.json
+++ b/nwg_panel/langs/en_US.json
@@ -49,6 +49,7 @@
"command": "Command",
"common": "Common",
"common-panel-settings": "Common nwg-panel settings",
+ "continuous": "Continuous",
"controls": "Controls",
"controls-window-width-tooltip": "Controls window width in pixels; leave 0 for auto.",
"cover-size": "Cover size",
@@ -80,6 +81,8 @@
"gust": "gust",
"header-icon-size": "Header icon size",
"height": "Height",
+ "hide-empty": "Hide empty workspaces",
+ "hide-other-outputs": "Hide workspaces on other outputs",
"homogeneous": "Homogeneous",
"homogeneous-tooltip": "Sets equal columns width be default if 'Modules center' not empty.",
"horizontal-padding": "Horizontal padding",
diff --git a/nwg_panel/main.py b/nwg_panel/main.py
index 33aa61d5..308b8a8f 100644
--- a/nwg_panel/main.py
+++ b/nwg_panel/main.py
@@ -207,12 +207,20 @@ def instantiate_content(panel, container, content_list, icons_path=""):
if item == "sway-workspaces":
if sway:
- if "sway-workspaces" in panel:
- workspaces = SwayWorkspaces(panel["sway-workspaces"], common.i3, icons_path=icons_path)
+ if "sway-workspaces" not in panel:
+ print("'sway-workspaces' not defined in this panel instance")
+ else:
+ output = None
+ if "output" in panel:
+ output = "{}".format(panel["output"])
+
+ workspaces = SwayWorkspaces(panel["sway-workspaces"], common.i3,
+ icons_path=icons_path,
+ output=output)
+
container.pack_start(workspaces, False, False, panel["items-padding"])
common.workspaces_list.append(workspaces)
- else:
- print("'sway-workspaces' not defined in this panel instance")
+
else:
print("'sway-workspaces' ignored")
diff --git a/nwg_panel/modules/executor.py b/nwg_panel/modules/executor.py
index c20066c9..4fa1b476 100644
--- a/nwg_panel/modules/executor.py
+++ b/nwg_panel/modules/executor.py
@@ -4,6 +4,8 @@
import threading
import signal
+from shlex import split
+
import gi
from gi.repository import GLib
@@ -26,9 +28,11 @@ def __init__(self, settings, icons_path, executor_name):
self.image = Gtk.Image()
self.label = Gtk.Label("")
self.icon_path = None
+ self.process = None
check_key(settings, "script", "")
check_key(settings, "interval", 0)
+ check_key(settings, "icon", "view-refresh-symbolic")
check_key(settings, "root-css-name", "root-executor")
check_key(settings, "css-name", "")
check_key(settings, "icon-placement", "left")
@@ -42,6 +46,7 @@ def __init__(self, settings, icons_path, executor_name):
check_key(settings, "angle", 0.0)
check_key(settings, "sigrt", signal.SIGRTMIN)
check_key(settings, "use-sigrt", False)
+ check_key(settings, "continuous", False)
self.label.set_angle(settings["angle"])
@@ -52,7 +57,10 @@ def __init__(self, settings, icons_path, executor_name):
if settings["angle"] != 0.0:
self.box.set_orientation(Gtk.Orientation.VERTICAL)
- update_image(self.image, "view-refresh-symbolic", self.settings["icon-size"], self.icons_path)
+ if self.settings["icon"] is None or len(self.settings["icon"]) == 0:
+ self.image.hide()
+ else:
+ update_image(self.image, self.settings["icon"], self.settings["icon-size"], self.icons_path)
self.set_property("name", settings["root-css-name"])
@@ -83,6 +91,7 @@ def __init__(self, settings, icons_path, executor_name):
def update_widget(self, output):
if output:
if len(output) == 1:
+ print("output1 {}".format(output))
if output[0].endswith(".svg") or output[0].endswith(".png"):
new_path = output[0].strip()
if new_path != self.icon_path:
@@ -110,7 +119,10 @@ def update_widget(self, output):
elif len(output) == 2:
new_path = output[0].strip()
- if "/" not in new_path and "." not in new_path: # name given instead of path
+ if new_path == "":
+ if self.image.get_visible():
+ self.image.hide()
+ elif "/" not in new_path and "." not in new_path: # name given instead of path
update_image(self.image, new_path, self.settings["icon-size"], self.icons_path)
self.icon_path = new_path
else:
@@ -137,9 +149,32 @@ def update_widget(self, output):
def get_output(self):
if "script" in self.settings and self.settings["script"]:
+ script = split(self.settings["script"])
+ continuous = self.settings["continuous"]
try:
- output = subprocess.check_output(self.settings["script"].split()).decode("utf-8").splitlines()
- GLib.idle_add(self.update_widget, output)
+ if not continuous:
+ subprocess.check_output(script)
+ output = subprocess.check_output(split(self.settings["script"])).decode("utf-8").splitlines()
+ GLib.idle_add(self.update_widget, output)
+ return
+
+ if self.process is not None and self.process.poll() is None:
+ # Last process has not yet finished
+ # Wait for it, possibly this is a continuous output
+ return
+
+ self.process = subprocess.Popen(script,
+ stdout = subprocess.PIPE)
+ first_line = None
+ while True:
+ line = self.process.stdout.readline().decode('utf-8')
+ if line is None or len(line) == 0: break
+
+ if first_line is None:
+ first_line = line
+ else:
+ GLib.idle_add(self.update_widget, [first_line, line])
+ first_line = None
except Exception as e:
print(e)
diff --git a/nwg_panel/modules/sway_workspaces.py b/nwg_panel/modules/sway_workspaces.py
index 42aa594f..af29d59f 100644
--- a/nwg_panel/modules/sway_workspaces.py
+++ b/nwg_panel/modules/sway_workspaces.py
@@ -7,10 +7,11 @@
class SwayWorkspaces(Gtk.Box):
- def __init__(self, settings, i3, icons_path):
+ def __init__(self, settings, i3, icons_path, output):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
self.settings = settings
self.i3 = i3
+ self.panel_output = output
self.num_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
self.ws_num2box = {}
self.ws_num2lbl = {}
@@ -34,6 +35,7 @@ def build_box(self):
check_key(self.settings, "mark-autotiling", True)
check_key(self.settings, "mark-content", True)
check_key(self.settings, "hide-empty", False)
+ check_key(self.settings, "hide-other-outputs", False)
check_key(self.settings, "show-layout", True)
check_key(self.settings, "angle", 0.0)
if self.settings["angle"] != 0.0:
@@ -42,27 +44,17 @@ def build_box(self):
# prevent from #142
ws_num = -1
if self.i3.get_tree().find_focused():
- ws_num, win_name, win_id, non_empty, win_layout, numbers = self.find_details()
-
- if len(self.settings["custom-labels"]) == 1 or len(self.settings["custom-labels"]) == len(self.settings["numbers"]):
- self.settings["custom-labels"] *= len(self.settings["numbers"])
- else:
- self.settings["custom-labels"] = []
-
- if len(self.settings["focused-labels"]) == 1 or len(self.settings["focused-labels"]) == len(self.settings["numbers"]):
- self.settings["focused-labels"] *= len(self.settings["numbers"])
- else:
- self.settings["focused-labels"] = []
+ ws_num, win_name, win_id, non_empty, win_layout, numbers, ws_defs = self.find_details()
self.pack_start(self.num_box, False, False, 0)
for idx, num in enumerate(self.settings["numbers"]):
- if num == str(ws_num) and self.settings["focused-labels"]:
- label = self.settings["focused-labels"][idx]
- elif self.settings["custom-labels"]:
- label = self.settings["custom-labels"][idx]
- else:
- label = str(num)
+ try:
+ int_num = int(num)
+ except:
+ int_num = 0
+
+ label = self.get_ws_label(int_num, focused = (str(num) == str(ws_num)))
eb, lbl = self.build_number(num, label)
self.num_box.pack_start(eb, False, False, 0)
@@ -114,9 +106,47 @@ def build_number(self, num, label):
return eb, lbl
+ def get_ws_label(self, num, idx = None, ws_defs = None, focused = False):
+ """
+ Get the text to display for a workspace:
+ - num: the number of the workspace (as integer)
+ - idx: the index of the workspace in ws_defs (if defined)
+ - focused: if the workspace is currently focused
+ - ws_defs: as returned by find_details()
+ """
+
+ # config_idx is the index of the current workspace in the
+ # configuration. It is the index of the workspace in the
+ # numbers config field, or the an index based on the
+ # workspace number, first workspace being number 1
+ config_idx = None
+ if num in self.settings["numbers"]:
+ config_idx = self.settings["numbers"].index(num)
+ elif len(self.settings["numbers"]) == 0:
+ config_idx = num - 1
+
+ if focused:
+ labels = self.settings["focused-labels"]
+ else:
+ labels = self.settings["custom-labels"]
+
+ if labels and config_idx in range(len(labels)):
+ text = labels[config_idx]
+ elif labels and len(labels) == 1:
+ text = labels[0]
+ elif idx in range(len(ws_defs)):
+ text = ws_defs[idx]['name']
+ else:
+ text = str(num)
+
+ return text
+
def refresh(self):
if self.i3.get_tree().find_focused():
- ws_num, win_name, win_id, non_empty, win_layout, numbers = self.find_details()
+ ws_num, win_name, win_id, non_empty, win_layout, numbers, ws_defs = self.find_details()
+
+ custom_labels = self.settings["custom-labels"]
+ focused_labels = self.settings["focused-labels"]
if len(self.settings["numbers"]) > 0:
numbers = self.settings["numbers"]
@@ -125,23 +155,15 @@ def refresh(self):
for num in self.ws_num2lbl:
self.ws_num2lbl[num].hide()
- for _idx, num in enumerate(numbers):
- idx = None
- if num in self.settings["numbers"]:
- idx = self.settings["numbers"].index(num)
+ for idx, num in enumerate(numbers):
+ focused = (str(num) == str(ws_num))
try:
int_num = int(num)
except:
int_num = 0
- if idx is None:
- text = str(num)
- elif num == str(ws_num) and self.settings["focused-labels"]:
- text = self.settings["focused-labels"][idx]
- elif self.settings["custom-labels"]:
- text = self.settings["custom-labels"][idx]
- else:
- text = str(num)
+ text = self.get_ws_label(int_num, idx, ws_defs,
+ focused = focused)
if num not in self.ws_num2lbl:
eb, lbl = self.build_number(num, text)
@@ -150,7 +172,7 @@ def refresh(self):
lbl = self.ws_num2lbl[num]
- if not self.settings["hide-empty"] or int_num in non_empty or num == str(ws_num):
+ if not self.settings["hide-empty"] or int_num in non_empty or focused:
lbl.show()
else:
lbl.hide()
@@ -163,10 +185,10 @@ def refresh(self):
else:
if text.endswith("."):
text = text[0:-1]
-
+
lbl.set_text(text)
- if num == str(ws_num):
+ if focused:
self.ws_num2box[num].set_property("name", "task-box-focused")
else:
self.ws_num2box[num].set_property("name", "task-box")
@@ -229,12 +251,30 @@ def find_details(self):
win_id = "" # app_id if available, else window_class
layout = None
numbers = []
+ ws_defs = []
for ws in workspaces:
- numbers.append(str(ws.num))
+ _, _, name = ws.name.partition(':')
+ if len(name) == 0:
+ name = str(ws.num)
+
+ hide_other_outputs = self.settings["hide-other-outputs"] and self.panel_output is not None
+ if hide_other_outputs and ws.output != self.panel_output:
+ continue
+
+ ws_defs.append({
+ 'num': int(ws.num),
+ 'name': name
+ })
if ws.focused:
ws_num = ws.num
+ # Sort ws_defs before constructing numbers and names to ensure
+ # dynamic workspaces always appear in sorted order
+ ws_defs.sort(key = lambda ws: ws['num'])
+ for _idx, ws in enumerate(ws_defs):
+ numbers.append(ws['num'])
+
non_empty = []
if self.settings["show-name"] or self.settings["show-icon"]:
f = self.i3.get_tree().find_focused()
@@ -273,16 +313,23 @@ def find_details(self):
if not layout:
layout = f.parent.layout
- return ws_num, win_name, win_id, non_empty, layout, numbers
+ return ws_num, win_name, win_id, non_empty, layout, numbers, ws_defs
def on_click(self, event_box, event_button, num):
nwg_panel.common.i3.command("workspace number {}".format(num))
def on_scroll(self, event_box, event):
+ hide_other_outputs = self.settings["hide-other-outputs"] and self.panel_output is not None
if event.direction == Gdk.ScrollDirection.UP:
- nwg_panel.common.i3.command("workspace prev")
+ if hide_other_outputs:
+ nwg_panel.common.i3.command("workspace prev_on_output")
+ else:
+ nwg_panel.common.i3.command("workspace prev")
elif event.direction == Gdk.ScrollDirection.DOWN:
- nwg_panel.common.i3.command("workspace next")
+ if hide_other_outputs:
+ nwg_panel.common.i3.command("workspace next_on_output")
+ else:
+ nwg_panel.common.i3.command("workspace next")
def on_enter_notify_event(self, widget, event):
widget.set_state_flags(Gtk.StateFlags.DROP_ACTIVE, clear=False)