Unverified Commit 3cf08d6b authored by giskard's avatar giskard Committed by GitHub

feat: add slider widget (#435)

* utils: expand `~` on execute_command() * feat: add widget slider * slider: queue setter to avoid racing * slider: queue get to avoid possible racing with set * slider: doc sync * slider: avoid dead loop on queue_get(), just in case * slider: expand cmd with predefined pattern * Ported slider to GTK4 * Added CSS to match the other widgets * Updated doc with information about "$value" --------- Co-authored-by: 's avatarErik Reider <35975961+ErikReider@users.noreply.github.com>
parent cf7fcaee
...@@ -360,6 +360,8 @@ $notification-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), ...@@ -360,6 +360,8 @@ $notification-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3),
@import "widgets/menubar"; @import "widgets/menubar";
/* Volume widget */ /* Volume widget */
@import "widgets/volume"; @import "widgets/volume";
/* Slider widget */
@import "widgets/slider";
/* Backlight widget */ /* Backlight widget */
@import "widgets/backlight"; @import "widgets/backlight";
/* Inhibitors widget */ /* Inhibitors widget */
......
.widget-slider {
padding: $margin;
margin: $margin;
border-radius: $border-radius;
label {
font-size: inherit;
}
}
...@@ -146,7 +146,7 @@ config file to be able to detect config errors ...@@ -146,7 +146,7 @@ config file to be able to detect config errors
type: bool ++ type: bool ++
default: true ++ default: true ++
description: Display notification timestamps relative to now e.g. \"26 minutes ago\". ++ description: Display notification timestamps relative to now e.g. \"26 minutes ago\". ++
If false, a local iso8601-formatted absolute timestamp is displayed. If false, a local iso8601-formatted absolute timestamp is displayed.
*control-center-height* ++ *control-center-height* ++
type: integer ++ type: integer ++
...@@ -242,6 +242,8 @@ config file to be able to detect config errors ...@@ -242,6 +242,8 @@ config file to be able to detect config errors
optional: true ++ optional: true ++
*buttons-grid*++ *buttons-grid*++
optional: true ++ optional: true ++
*slider*++
optional: true ++
#START pulse-audio #START pulse-audio
*volume*++ *volume*++
optional: true ++ optional: true ++
...@@ -501,6 +503,53 @@ config file to be able to detect config errors ...@@ -501,6 +503,53 @@ config file to be able to detect config errors
description: Wether the toggle button is active as default or not ++ description: Wether the toggle button is active as default or not ++
description: A list of actions containing a label and a command ++ description: A list of actions containing a label and a command ++
description: A grid of buttons that execute shell commands ++ description: A grid of buttons that execute shell commands ++
*slider*++
type: object ++
css class: widget-slider ++
properties: ++
label: ++
type: string ++
optional: true ++
default: "slider" ++
description: Text displayed in front of the slider ++
cmd_setter: ++
type: string ++
optional: true ++
default: "" ++
description: command to set the value. Use $value to get ++
the current value. ++
cmd_getter: ++
type: string ++
optional: true ++
default: "" ++
description: command to get the actual value. ++
Use $value to get the current value. ++
min: ++
type: integer ++
optional: true ++
default: 0 ++
description: minimum value of the slider range ++
max: ++
type: integer ++
optional: true ++
default: 100 ++
description: maximum value of the slider range ++
min_limit: ++
type: integer ++
optional: true ++
default: 0 ++
description: limit minimum value of the slider ++
max_limit: ++
type: integer ++
optional: true ++
default: 100 ++
description: limit maximum value of the slider ++
value_scale: ++
type: integer ++
optional: true ++
default: 0 ++
description: scale small value, slider round digits ++
description: general slider control ++
#START pulse-audio #START pulse-audio
*volume*++ *volume*++
type: object ++ type: object ++
......
...@@ -339,6 +339,9 @@ ...@@ -339,6 +339,9 @@
"^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/menubar" "$ref": "#/widgets/menubar"
}, },
"^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/slider"
},
"^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/volume" "$ref": "#/widgets/volume"
}, },
...@@ -466,9 +469,9 @@ ...@@ -466,9 +469,9 @@
"default": "" "default": ""
}, },
"active": { "active": {
"type": "boolean", "type": "boolean",
"description": "Wether the toggle button is active as default or not", "description": "Wether the toggle button is active as default or not",
"default": false "default": false
} }
} }
} }
...@@ -502,13 +505,13 @@ ...@@ -502,13 +505,13 @@
"description": "Animation type for menu", "description": "Animation type for menu",
"enum": ["slide_down", "slide_up", "none"] "enum": ["slide_down", "slide_up", "none"]
}, },
"animation-duration":{ "animation-duration": {
"type": "integer", "type": "integer",
"default": 250, "default": 250,
"description": "Duration of animation in milliseconds" "description": "Duration of animation in milliseconds"
}, },
"actions": { "actions": {
"$ref" : "#/widgets/buttons-grid/properties/actions" "$ref": "#/widgets/buttons-grid/properties/actions"
} }
} }
}, },
...@@ -524,12 +527,59 @@ ...@@ -524,12 +527,59 @@
"enum": ["right", "left"] "enum": ["right", "left"]
}, },
"actions": { "actions": {
"$ref" : "#/widgets/buttons-grid/properties/actions" "$ref": "#/widgets/buttons-grid/properties/actions"
} }
} }
} }
} }
}, },
"slider": {
"type": "object",
"description": "general slider control",
"additionalProperties": false,
"properties": {
"label": {
"type": "string",
"description": "Text displayed in front of the slider",
"default": "slider"
},
"cmd_setter": {
"type": "string",
"description": "command to set the value. Use $value to get the current value",
"default": ""
},
"cmd_getter": {
"type": "string",
"description": "command to get the actual value. Use $value to get the current value",
"default": ""
},
"min": {
"type": "integer",
"description": "minimum value of the slider range",
"default": 0
},
"max": {
"type": "integer",
"description": "maximum value of the slider range",
"default": 100
},
"min_limit": {
"type": "integer",
"description": "limit minimum value of the slider",
"default": 0
},
"max_limit": {
"type": "integer",
"description": "limit maximum value of the slider",
"default": 100
},
"value_scale": {
"type": "integer",
"default": 0,
"description": "scale small value, slider round digits"
}
}
},
"volume": { "volume": {
"type": "object", "type": "object",
"description": "Slider to control pulse volume", "description": "Slider to control pulse volume",
...@@ -582,7 +632,7 @@ ...@@ -582,7 +632,7 @@
"description": "Animation type for menu", "description": "Animation type for menu",
"enum": ["slide_down", "slide_up", "none"] "enum": ["slide_down", "slide_up", "none"]
}, },
"animation-duration":{ "animation-duration": {
"type": "integer", "type": "integer",
"default": 250, "default": 250,
"description": "Duration of animation in milliseconds" "description": "Duration of animation in milliseconds"
......
...@@ -26,6 +26,9 @@ namespace SwayNotificationCenter.Widgets { ...@@ -26,6 +26,9 @@ namespace SwayNotificationCenter.Widgets {
case "buttons-grid": case "buttons-grid":
widget = new ButtonsGrid (suffix, swaync_daemon, noti_daemon); widget = new ButtonsGrid (suffix, swaync_daemon, noti_daemon);
break; break;
case "slider":
widget = new Slider (suffix, swaync_daemon, noti_daemon);
break;
#if HAVE_PULSE_AUDIO #if HAVE_PULSE_AUDIO
case "volume": case "volume":
widget = new Volume (suffix, swaync_daemon, noti_daemon); widget = new Volume (suffix, swaync_daemon, noti_daemon);
......
namespace SwayNotificationCenter.Widgets {
public class Slider : BaseWidget {
public override string widget_name {
get {
return "slider";
}
}
Gtk.Label label_widget = new Gtk.Label (null);
Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);
private double min_limit;
private double max_limit;
private double ? last_set;
private bool cmd_ing = false;
private string cmd_setter;
private string cmd_getter;
public Slider (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
base (suffix, swaync_daemon, noti_daemon);
int ? round_digits = 0;
Json.Object ? config = get_config (this);
if (config != null) {
string ? label = get_prop<string> (config, "label");
label_widget.set_label (label ?? "Slider");
cmd_setter = get_prop<string> (config, "cmd_setter") ?? "";
cmd_getter = get_prop<string> (config, "cmd_getter") ?? "";
int ? min = get_prop<int> (config, "min");
int ? max = get_prop<int> (config, "max");
int ? maxl = get_prop<int> (config, "max_limit");
int ? minl = get_prop<int> (config, "min_limit");
round_digits = get_prop<int> (config, "value_scale");
if (min == null)min = 0;
if (max == null)max = 100;
if (round_digits == null)round_digits = 0;
max_limit = maxl != null ? double.min (max, maxl) : max;
min_limit = minl != null ? double.max (min, minl) : min;
double scale = Math.pow (10, round_digits);
min_limit /= scale;
max_limit /= scale;
slider.set_range (min / scale, max / scale);
}
slider.set_draw_value (false);
slider.set_round_digits (round_digits);
slider.set_hexpand (true);
slider.set_halign (Gtk.Align.FILL);
slider.value_changed.connect (() => {
double value = slider.get_value ();
if (value > max_limit)
value = max_limit;
if (value < min_limit)
value = min_limit;
slider.set_value (value);
slider.tooltip_text = value.to_string ();
if (cmd_setter != "" && last_set != value) {
last_set = value;
queue_set.begin (value);
}
});
append (label_widget);
append (slider);
}
public string expand_cmd (string cmd, string regex, string value) {
try {
Regex _regx = new Regex (Regex.escape_string (regex));
return _regx.replace_literal (cmd, -1, 0, value);
} catch (Error e) {
warning ("failed to expand cmd: %s\n", e.message);
return cmd;
}
}
public async void queue_set (double value) {
if (cmd_ing)
return;
cmd_ing = true;
var cmd = expand_cmd (cmd_setter, "$value", value.to_string ());
yield Functions.execute_command (cmd, {}, null);
cmd_ing = false;
// make sure the last_set is applied
if (value != last_set) {
yield queue_set (last_set);
}
}
public async void queue_get (int retry, out string value) {
if (cmd_ing) {
if (retry <= 0) {
value = "";
return;
}
yield queue_get (retry - 1, out value);
}
cmd_ing = true;
yield Functions.execute_command (cmd_getter, {}, out value);
cmd_ing = false;
}
public async void on_update () {
if (cmd_getter == "")
return;
string value_str = "";
yield queue_get (4, out value_str);
double value = double.parse (value_str);
if (value <= max_limit && value >= min_limit) {
last_set = value;
slider.set_value (value);
}
}
public override void on_cc_visibility_change (bool value) {
if (value)
on_update.begin ();
}
}
}
...@@ -280,6 +280,9 @@ namespace SwayNotificationCenter { ...@@ -280,6 +280,9 @@ namespace SwayNotificationCenter {
string[] argvp; string[] argvp;
Shell.parse_argv ("/bin/sh -c \"%s\"".printf (cmd), out argvp); Shell.parse_argv ("/bin/sh -c \"%s\"".printf (cmd), out argvp);
if (argvp[0].has_prefix ("~"))
argvp[0] = Environment.get_home_dir () + argvp[0].substring (1);
Pid child_pid; Pid child_pid;
int std_output; int std_output;
Process.spawn_async_with_pipes ( Process.spawn_async_with_pipes (
......
...@@ -41,6 +41,8 @@ widget_sources = [ ...@@ -41,6 +41,8 @@ widget_sources = [
'controlCenter/widgets/menubar/menubar.vala', 'controlCenter/widgets/menubar/menubar.vala',
# Widget: Buttons Grid # Widget: Buttons Grid
'controlCenter/widgets/buttonsGrid/buttonsGrid.vala', 'controlCenter/widgets/buttonsGrid/buttonsGrid.vala',
# Widget: Slider
'controlCenter/widgets/slider/slider.vala',
# Widget: Backlight Slider # Widget: Backlight Slider
'controlCenter/widgets/backlight/backlight.vala', 'controlCenter/widgets/backlight/backlight.vala',
'controlCenter/widgets/backlight/backlightUtil.vala', 'controlCenter/widgets/backlight/backlightUtil.vala',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment