Files
nwg-panel/nwg_panel/modules/brightness_slider.py

296 lines
11 KiB
Python

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GLib, GtkLayerShell
from nwg_panel.tools import check_key, get_brightness, set_brightness, update_image, create_background_task, eprint
class BrightnessSlider(Gtk.EventBox):
def __init__(self, settings, icons_path=""):
Gtk.EventBox.__init__(self)
defaults = {
"show-values": True,
"icon-size": 16,
"interval": 10,
"hover-opens": False,
"leave-closes": False,
"root-css-name": "brightness-module",
"css-name": "brightness-popup",
"angle": 0.0,
"icon-placement": "start",
"backlight-device": "",
"backlight-controller": "brightnessctl",
"slider-orientation": "horizontal",
"slider-inverted": False,
"popup-icon-placement": "start",
"popup-horizontal-alignment": "left",
"popup-vertical-alignment": "top",
"popup-width": 256,
"popup-height": 64,
"popup-horizontal-margin": 0,
"popup-vertical-margin": 0,
"step-size": 1,
}
for key in defaults:
check_key(settings, key, defaults[key])
self.settings = settings
self.set_property("name", self.settings["root-css-name"])
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
self.add(self.box)
self.icons_path = icons_path
self.icon_size = settings["icon-size"]
self.bri_icon_name = "view-refresh-symbolic"
self.bri_image = Gtk.Image.new_from_icon_name(self.bri_icon_name, Gtk.IconSize.MENU)
self.bri_label = Gtk.Label() if settings["show-values"] else None
self.bri_value = 0
self.popup_window = PopupWindow(self, settings, icons_path=self.icons_path)
if settings["angle"] != 0.0:
self.box.set_orientation(Gtk.Orientation.VERTICAL)
self.bri_label.set_angle(settings["angle"])
# events
self.connect('button-release-event', self.on_button_release)
self.connect('enter-notify-event', self.on_enter_notify_event)
self.connect('leave-notify-event', self.on_leave_notify_event)
if self.settings["step-size"] > 0:
self.add_events(Gdk.EventMask.SCROLL_MASK)
self.connect('scroll-event', self.on_scroll)
self.build_box()
self.refresh()
def build_box(self):
if self.settings["icon-placement"] == "start":
self.box.pack_start(self.bri_image, False, False, 2)
if self.bri_label:
self.box.pack_start(self.bri_label, False, False, 2)
if self.settings["icon-placement"] == "end":
self.box.pack_start(self.bri_image, False, False, 2)
def refresh(self):
thread = create_background_task(self.refresh_output, self.settings["interval"])
thread.start()
def refresh_output(self):
try:
self.bri_value = get_brightness(device=self.settings["backlight-device"],
controller=self.settings["backlight-controller"])
GLib.idle_add(self.update_brightness)
except Exception as e:
eprint(e)
def update_brightness(self, get=True):
icon_name = bri_icon_name(self.bri_value)
if icon_name != self.bri_icon_name:
update_image(self.bri_image, icon_name, self.icon_size, self.icons_path)
self.bri_icon_name = icon_name
if self.bri_label:
self.bri_label.set_text("{}%".format(self.bri_value))
if get:
self.popup_window.refresh()
def on_button_release(self, w, event):
if not self.popup_window.get_visible():
self.popup_window.show_all()
else:
self.popup_window.hide()
return False
def on_scroll(self, w, event):
if event.direction == Gdk.ScrollDirection.UP:
self.bri_value += self.settings["step-size"]
elif event.direction == Gdk.ScrollDirection.DOWN:
self.bri_value -= self.settings["step-size"]
self.bri_value = min(self.bri_value, 100)
self.bri_value = max(self.bri_value, 0)
self.update_brightness(get=False)
if self.popup_window.get_visible():
self.popup_window.bri_scale.set_value(self.bri_value)
set_brightness(self.bri_value, device=self.settings["backlight-device"],
controller=self.settings["backlight-controller"])
return False
def on_enter_notify_event(self, widget, event):
if self.settings["hover-opens"]:
if not self.popup_window.get_visible():
self.popup_window.show_all()
else:
widget.set_state_flags(Gtk.StateFlags.DROP_ACTIVE, clear=False)
widget.set_state_flags(Gtk.StateFlags.SELECTED, clear=False)
# cancel popup window close, as it's probably unwanted ATM
self.popup_window.on_window_enter()
return True
def on_leave_notify_event(self, widget, event):
widget.unset_state_flags(Gtk.StateFlags.DROP_ACTIVE)
widget.unset_state_flags(Gtk.StateFlags.SELECTED)
return True
class PopupWindow(Gtk.Window):
def __init__(self, parent, settings, monitor=None, icons_path=""):
Gtk.Window.__init__(self, type_hint=Gdk.WindowTypeHint.NORMAL)
GtkLayerShell.init_for_window(self)
if monitor:
GtkLayerShell.set_monitor(self, monitor)
self.parent = parent
self.settings = settings
self.icon_size = settings["icon-size"]
self.icons_path = icons_path
self.src_tag = 0
self.value_changed = False
self.scrolled = False
self.set_property("name", self.settings["css-name"])
self.connect("show", self.on_window_show)
if settings["leave-closes"]:
self.connect("leave_notify_event", self.on_window_exit)
self.connect("enter_notify_event", self.on_window_enter)
eb = Gtk.EventBox()
eb.set_above_child(False)
self.box = Gtk.Box(spacing=0)
if self.settings["slider-orientation"] == "vertical":
self.box.set_orientation(Gtk.Orientation.VERTICAL)
self.bri_scale = Gtk.Scale.new_with_range(orientation=Gtk.Orientation.VERTICAL, min=0, max=100, step=1)
elif self.settings["slider-orientation"] == "horizontal":
self.box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.bri_scale = Gtk.Scale.new_with_range(orientation=Gtk.Orientation.HORIZONTAL, min=0, max=100, step=1)
self.bri_scale.set_inverted(self.settings["slider-inverted"])
if self.settings["backlight-controller"] == "ddcutil":
self.bri_scale_handler = self.bri_scale.connect("value-changed", self.on_value_changed)
self.bri_scale.connect("button-release-event", self.on_button_release)
self.bri_scale.add_events(Gdk.EventMask.SCROLL_MASK)
self.bri_scale.connect('scroll-event', self.on_scroll)
else:
self.bri_scale_handler = self.bri_scale.connect("value-changed", self.set_bri)
self.bri_icon_name = "view-refresh-symbolic"
self.bri_image = Gtk.Image.new_from_icon_name(self.bri_icon_name, Gtk.IconSize.MENU)
eb.add(self.box)
self.add(eb)
if settings["popup-vertical-alignment"] == "top":
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, settings["popup-vertical-margin"])
elif settings["popup-vertical-alignment"] == "bottom":
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.BOTTOM, settings["popup-vertical-margin"])
if settings["popup-horizontal-alignment"] == "left":
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.LEFT, settings["popup-horizontal-margin"])
elif settings["popup-horizontal-alignment"] == "right":
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.RIGHT, settings["popup-horizontal-margin"])
Gtk.Widget.set_size_request(self.box, settings["popup-width"], settings["popup-height"])
self.build_box()
Gdk.threads_add_timeout(GLib.PRIORITY_LOW, 500, self.refresh)
def build_box(self):
if self.settings["popup-icon-placement"] == "start":
self.box.pack_start(self.bri_image, False, False, 6)
self.box.pack_start(self.bri_scale, True, True, 5)
if self.settings["popup-icon-placement"] == "end":
self.box.pack_start(self.bri_image, False, False, 6)
def refresh(self, *args):
if self.get_visible():
if not self.value_changed:
self.bri_scale.set_value(self.parent.bri_value)
if self.parent.bri_icon_name != self.bri_icon_name:
update_image(self.bri_image, self.parent.bri_icon_name, self.icon_size, self.icons_path)
self.bri_icon_name = self.parent.bri_icon_name
else:
with self.bri_scale.handler_block(self.bri_scale_handler):
self.bri_scale.set_value(self.parent.bri_value)
return True
def on_window_exit(self, w, e):
if self.get_visible():
self.src_tag = GLib.timeout_add_seconds(1, self.hide_and_clear_tag)
return True
def hide_and_clear_tag(self):
self.hide()
self.src_tag = 0
def on_window_enter(self, *args):
if self.src_tag > 0:
GLib.Source.remove(self.src_tag)
self.src_tag = 0
return True
def on_window_show(self, *args):
self.src_tag = 0
self.refresh()
def set_bri(self, slider):
self.parent.bri_value = int(slider.get_value())
self.parent.update_brightness(get=False)
set_brightness(self.parent.bri_value, device=self.settings["backlight-device"],
controller=self.settings["backlight-controller"])
def on_button_release(self, scale, event):
if self.value_changed:
self.set_bri(scale)
self.value_changed = False
def on_value_changed(self, *args):
if self.scrolled:
self.set_bri(self.bri_scale)
self.scrolled = False
else:
self.value_changed = True
def on_scroll(self, w, event):
self.scrolled = True
def bri_icon_name(value):
icon_name = "display-brightness-low-symbolic"
if value > 70:
icon_name = "display-brightness-high-symbolic"
elif value > 30:
icon_name = "display-brightness-medium-symbolic"
return icon_name