widgets: remove dnd, buttons-grid, menubar and power-profiles widgets

parent 965f4092
...@@ -18,7 +18,6 @@ theme might require extra tweaks to the default CSS style file* ...@@ -18,7 +18,6 @@ theme might require extra tweaks to the default CSS style file*
* [Run](#run) * [Run](#run)
* [Control Center Shortcuts](#control-center-shortcuts) * [Control Center Shortcuts](#control-center-shortcuts)
* [Configuring](#configuring) * [Configuring](#configuring)
* [Toggle Buttons](#toggle-buttons)
* [Notification Inhibition](#notification-inhibition) * [Notification Inhibition](#notification-inhibition)
* [Waybar Example](#waybar-example) * [Waybar Example](#waybar-example)
* [Debugging Environment Variables](#debugging-environment-variables) * [Debugging Environment Variables](#debugging-environment-variables)
...@@ -55,8 +54,7 @@ These widgets can be customized, added, removed and even reordered ...@@ -55,8 +54,7 @@ These widgets can be customized, added, removed and even reordered
- Notifications (Will always be visible) - Notifications (Will always be visible)
- Label - Label
- Mpris (Media player controls for Spotify, Firefox, Chrome, etc...) - Mpris (Media player controls for Spotify, Firefox, Chrome, etc...)
- Menubar with dropdown and buttons - Quick Settings (GNOME-style toggle tiles)
- Button grid
- Volume slider using PulseAudio - Volume slider using PulseAudio
- Backlight slider - Backlight slider
...@@ -175,32 +173,6 @@ and customize them instead. To use the SCSS files, compile with `sassc`. ...@@ -175,32 +173,6 @@ and customize them instead. To use the SCSS files, compile with `sassc`.
**Tip**: running ximper-shell-notification-center with `GTK_DEBUG=interactive ximper-shell-notification-center` will open a inspector **Tip**: running ximper-shell-notification-center with `GTK_DEBUG=interactive ximper-shell-notification-center` will open a inspector
window that'll allow you to see all of the CSS classes + other information. window that'll allow you to see all of the CSS classes + other information.
## Toggle Buttons
To add toggle buttons to your control center you can set the "type" of any action to "toggle".
The toggle button supports different commands depending on the state of the button and
an "update-command" to update the state in case of changes from outside the notification center. The update-command
is called every time the control center is opened.
The active toggle button also gains the css-class ".toggle:checked"
`config.json` example:
```jsonc
{
"buttons-grid": { // also works with actions in menubar widget
"actions": [
{
"label": "WiFi",
"type": "toggle",
"active": true,
"command": "sh -c '[[ $XSNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
"update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
}
]
}
}
```
## Notification Inhibition ## Notification Inhibition
Notifications can be inhibited through the provided `ximper-shell-notification-center-client` executable Notifications can be inhibited through the provided `ximper-shell-notification-center-client` executable
......
...@@ -380,16 +380,10 @@ notificationwindow, blankwindow { ...@@ -380,16 +380,10 @@ notificationwindow, blankwindow {
/* Title widget */ /* Title widget */
@import "widgets/title"; @import "widgets/title";
/* DND widget */
@import "widgets/dnd";
/* Label widget */ /* Label widget */
@import "widgets/label"; @import "widgets/label";
/* Mpris widget */ /* Mpris widget */
@import "widgets/mpris"; @import "widgets/mpris";
/* Buttons widget */
@import "widgets/buttons";
/* Menubar widget */
@import "widgets/menubar";
/* Volume widget */ /* Volume widget */
@import "widgets/volume"; @import "widgets/volume";
/* Slider widget */ /* Slider widget */
......
.widget-buttons-grid {
& flowboxchild>button {
border-radius: var(--border-radius);
&.toggle:checked {
/* style given to the active toggle button */
}
&:hover {}
}
}
.widget-dnd {
label {
color: var(--text-color);
margin-right: $margin;
font-size: 1.1rem;
}
switch {
border-radius: var(--border-radius);
margin-left: $margin;
&:checked {}
slider {
border-radius: var(--border-radius);
}
}
}
.widget-menubar {
& > .menu-button-bar {
/* The left button container */
& > .start {
margin-left: $margin;
}
/* The right button container */
& > .end {
margin-right: $margin;
}
/* The left and right button container */
& > .widget-menubar-container {
button {
border-radius: var(--border-radius);
margin: 0 $margin / 2;
}
}
}
/* The revealer buttons */
& > revealer {
* {
margin-top: $margin;
button {
border-radius: var(--border-radius);
margin: $margin;
margin-top: 0;
&:last-child {
margin-bottom: 0;
}
}
}
}
/* .AnyName { Name defined in config after #
background-color: rgba(var(--noti-bg), 1.0);
padding: 8px;
margin: 8px;
border-radius: 12px;
}
.AnyName>button {
background: transparent;
border: none;
}
.AnyName>button:hover {
background-color: var(--noti-bg-hover);
} */
}
...@@ -329,9 +329,7 @@ config file to be able to detect config errors ...@@ -329,9 +329,7 @@ config file to be able to detect config errors
optional: true ++ optional: true ++
*mpris*++ *mpris*++
optional: true ++ optional: true ++
*menubar*++ *quick-settings*++
optional: true ++
*buttons-grid*++
optional: true ++ optional: true ++
*slider*++ *slider*++
optional: true ++ optional: true ++
...@@ -397,16 +395,6 @@ config file to be able to detect config errors ...@@ -397,16 +395,6 @@ config file to be able to detect config errors
default: true ++ default: true ++
description: Whether to display a "Clear All" button ++ description: Whether to display a "Clear All" button ++
description: The notification visibility state. ++ description: The notification visibility state. ++
*dnd*++
type: object ++
css class: widget-dnd ++
properties: ++
text: ++
type: string ++
optional: true ++
default: "Do Not Disturb" ++
description: The title of the widget ++
description: Control Center Do Not Disturb Widget. ++
*label*++ *label*++
type: object ++ type: object ++
css class: widget-label ++ css class: widget-label ++
...@@ -459,147 +447,6 @@ config file to be able to detect config errors ...@@ -459,147 +447,6 @@ config file to be able to detect config errors
default: false ++ default: false ++
description: Whether to loop through the mpris carousel. ++ description: Whether to loop through the mpris carousel. ++
description: A widget that displays multiple music players. ++ description: A widget that displays multiple music players. ++
*menubar*++
type: object ++
css classes: ++
widget-menubar ++
.widget-menubar>box>.menu-button-bar ++
name of element given after menu or buttons with # ++
patternProperties: ++
menu#<name>: ++
type: object ++
properties: ++
label: ++
type: string ++
optional: true ++
default: "Menu" ++
description: Label of button to show/hide menu dropdown ++
position: ++
type: string ++
optional: true ++
default: "right" ++
description: Horizontal position of the button in the bar ++
enum: ["right", "left"] ++
animation-type: ++
type: string ++
optional: true ++
default: "slide_down" ++
description: Animation type for menu++
enum: ["slide_down", "slide_up", "none"] ++
animation-duration: ++
type: integer ++
optional: true ++
default: 250 ++
description: Duration of animation in milliseconds ++
actions: ++
type: array ++
Default values: [] ++
Valid array values: ++
type: object ++
properties: ++
label: ++
type: string ++
default: "label" ++
description: Text to be displayed in button ++
command: ++
type: string ++
default: "" ++
description: "Command to be executed on click" ++
type: ++
type: string ++
default: "normal" ++
description: Type of the button. ++
Toggle buttons receive the '.active' css class ++
enum: ["normal", "toggle"] ++
update-command: ++
type: string ++
default: "" ++
description: "Command to be executed on visibility change of ++
cc to update the active state of the toggle button (should ++
echo true or false)" ++
active: ++
type: bool ++
default: false ++
description: Wether the toggle button is active as default or not ++
description: A list of actions containing a label and a command ++
description: A button to reveal a dropdown with action-buttons ++
buttons#<name>: ++
type: object ++
properties: ++
position: ++
type: string ++
optional: true ++
default: "right" ++
description: Horizontal position of the buttons in the bar ++
enum: ["right", "left"] ++
actions: ++
type: array ++
Default values: [] ++
Valid array values: ++
type: object ++
properties: ++
label: ++
type: string ++
default: "label" ++
description: Text to be displayed in button ++
command: ++
type: string ++
default: "" ++
description: "Command to be executed on click" ++
type: ++
type: string ++
default: "normal" ++
description: Type of the button ++
Toggle buttons receive the '.active' css class and an env ++
variable "XSNC_TOGGLE_STATE" is set. See example usage in the ++
default config.json ++
enum: ["normal", "toggle"] ++
update-command: ++
type: string ++
default: "" ++
description: "Command to be executed on visibility change of ++
cc to update the active state of the toggle button (should ++
echo true or false)" ++
active: ++
type: bool ++
default: false ++
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 buttons to be displayed in the menu-button-bar ++
*buttons-grid*++
type: object ++
css class: widget-buttons (access buttons with >flowbox>flowboxchild>button) ++
properties: ++
buttons-per-row: ++
type: number ++
actions: ++
type: array ++
Default values: [] ++
Valid array values: ++
type: object ++
properties: ++
label: ++
type: string ++
default: "label" ++
description: Text to be displayed in button ++
command: ++
type: string ++
default: "" ++
description: "Command to be executed on click" ++
type: ++
type: string ++
default: "normal" ++
description: Type of the button ++
Toggle buttons receive the '.active' css class and an env ++
variable "XSNC_TOGGLE_STATE" is set. See example usage in the ++
default config.json ++
enum: ["normal", "toggle"] ++
active: ++
type: bool ++
default: false ++
description: Wether the toggle button is active as default or not ++
description: A list of actions containing a label and a command ++
description: A grid of buttons that execute shell commands ++
*slider*++ *slider*++
type: object ++ type: object ++
css class: widget-slider ++ css class: widget-slider ++
...@@ -760,38 +607,6 @@ config file to be able to detect config errors ...@@ -760,38 +607,6 @@ config file to be able to detect config errors
"show-album-art": "always", "show-album-art": "always",
"loop-carousel": false "loop-carousel": false
}, },
"menubar": {
"menu#power": {
"label": "Power",
"position": "right",
"actions": [
{
"label": "Shut down",
"command": "systemctl poweroff"
},
...
]
},
"buttons#screenshot": {
"position": "left",
"actions": [
{
"label": "Screenshot",
"command": "grim"
},
...
]
}
},
"buttons": {
"actions": [
{
"label": "wifi",
"command": "rofi-wifi-menu"
},
...
]
}
} }
} }
``` ```
src/main.vala src/main.vala
src/notification/notification.vala src/notification/notification.vala
src/controlCenter/widgets/title/title.vala src/controlCenter/widgets/title/title.vala
src/controlCenter/widgets/dnd/dnd.vala
src/controlCenter/widgets/inhibitors/inhibitors.vala src/controlCenter/widgets/inhibitors/inhibitors.vala
src/controlCenter/widgets/volume/volume.vala src/controlCenter/widgets/volume/volume.vala
src/controlCenter/widgets/backlight/backlight.vala src/controlCenter/widgets/backlight/backlight.vala
src/controlCenter/widgets/slider/slider.vala src/controlCenter/widgets/slider/slider.vala
src/controlCenter/widgets/menubar/menubar.vala src/controlCenter/widgets/quickSettings/quickSettings.vala
src/controlCenter/widgets/powerProfiles/powerProfiles.vala src/controlCenter/widgets/quickSettings/tiles/dndTile.vala
src/controlCenter/widgets/quickSettings/tiles/darkModeTile.vala
src/controlCenter/widgets/quickSettings/tiles/powerProfilesTile.vala
src/controlCenter/widgets/quickSettings/tiles/commandTile.vala
src/controlCenter/widgets/quickSettings/tiles/caffeineTile.vala
src/controlCenter/widgets/quickSettings/tiles/nightLightTile.vala
data/ui/notifications_widget.blp data/ui/notifications_widget.blp
...@@ -87,10 +87,6 @@ msgstr "Яркость" ...@@ -87,10 +87,6 @@ msgstr "Яркость"
msgid "Slider" msgid "Slider"
msgstr "Ползунок" msgstr "Ползунок"
#: src/controlCenter/widgets/menubar/menubar.vala:264
msgid "Menu"
msgstr "Меню"
#: src/controlCenter/widgets/powerProfiles/powerProfiles.vala:149 #: src/controlCenter/widgets/powerProfiles/powerProfiles.vala:149
msgid "Performance" msgid "Performance"
msgstr "Производительность" msgstr "Производительность"
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
"widgets": [ "widgets": [
"inhibitors", "inhibitors",
"title", "title",
"dnd",
"notifications" "notifications"
], ],
"widget-config": { "widget-config": {
...@@ -56,9 +55,6 @@ ...@@ -56,9 +55,6 @@
"text": "Notifications", "text": "Notifications",
"clear-all-button": true "clear-all-button": true
}, },
"dnd": {
"text": "Do Not Disturb"
},
"label": { "label": {
"max-lines": 5, "max-lines": 5,
"text": "Label Text" "text": "Label Text"
...@@ -68,18 +64,6 @@ ...@@ -68,18 +64,6 @@
"autohide": false, "autohide": false,
"show-album-art": "always", "show-album-art": "always",
"loop-carousel": false "loop-carousel": false
},
"buttons-grid": {
"buttons-per-row": 7,
"actions": [
{
"label": "直",
"type": "toggle",
"active": true,
"command": "sh -c '[[ $XSNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
"update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
}
]
} }
} }
} }
...@@ -309,7 +309,7 @@ ...@@ -309,7 +309,7 @@
"type": "array", "type": "array",
"uniqueItems": true, "uniqueItems": true,
"description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.", "description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.",
"default": ["inhibitors", "title", "dnd", "notifications"], "default": ["inhibitors", "title", "notifications"],
"items": { "items": {
"type": "string", "type": "string",
"$comment": "Sadly can't use regex and enums at the same time. Fix in the future?", "$comment": "Sadly can't use regex and enums at the same time. Fix in the future?",
...@@ -330,21 +330,12 @@ ...@@ -330,21 +330,12 @@
"$comment": "References the widget structure from \"widgets\" below", "$comment": "References the widget structure from \"widgets\" below",
"$ref": "#/widgets/title" "$ref": "#/widgets/title"
}, },
"^dnd(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/dnd"
},
"^label(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^label(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/label" "$ref": "#/widgets/label"
}, },
"^mpris(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^mpris(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/mpris" "$ref": "#/widgets/mpris"
}, },
"^buttons-grid(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/buttons-grid"
},
"^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/menubar"
},
"^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"$ref": "#/widgets/slider" "$ref": "#/widgets/slider"
}, },
...@@ -392,18 +383,6 @@ ...@@ -392,18 +383,6 @@
} }
} }
}, },
"dnd": {
"type": "object",
"description": "Control Center Do Not Disturb Widget",
"additionalProperties": false,
"properties": {
"text": {
"type": "string",
"description": "The title of the widget",
"default": "Do Not Disturb"
}
}
},
"label": { "label": {
"type": "object", "type": "object",
"description": "A generic widget that allows the user to add custom text", "description": "A generic widget that allows the user to add custom text",
...@@ -458,143 +437,6 @@ ...@@ -458,143 +437,6 @@
} }
} }
}, },
"buttons-grid": {
"type": "object",
"description": "A widget to add a grid of buttons that execute shell commands",
"additionalProperties": false,
"properties": {
"buttons-per-row": {
"type": "number",
"description": "How many buttons should be shown in a buttons-grid row"
},
"actions": {
"type": "array",
"description": "A list of actions containing a label and a command",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Text to be displayed in button",
"default": "label"
},
"command": {
"type": "string",
"description": "Command to be executed on click",
"default": ""
},
"type": {
"type": "string",
"description": "Type of the button; toggle buttons receive the .active css class and an env variable 'XSNC_TOGGLE_STATE' is set. See example in the default config.json",
"default": "normal",
"enum": ["normal", "toggle"]
},
"update-command": {
"type": "string",
"description": "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)",
"default": ""
},
"active": {
"type": "boolean",
"description": "Whether the toggle button is active as default or not",
"default": false
}
}
}
}
}
},
"menubar": {
"type": "object",
"description": "A bar that contains action-buttons and buttons to open a dropdown with action-buttons",
"additionalProperties": false,
"patternProperties": {
"^menu(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"type": "object",
"description": "A button that opens a dropdown with action-buttons",
"additionalProperties": false,
"properties": {
"label": {
"type": "string",
"description": "Text to be displayed in button",
"default": "Menu"
},
"position": {
"type": "string",
"description": "Horizontal position of the button in the bar",
"default": "right",
"enum": ["right", "left"]
},
"animation-type": {
"type": "string",
"default": "slide_down",
"description": "Animation type for menu",
"enum": ["slide_down", "slide_up", "none"]
},
"animation-duration": {
"type": "integer",
"default": 250,
"description": "Duration of animation in milliseconds"
},
"actions": {
"$ref": "#/widgets/buttons-grid/properties/actions"
}
}
},
"^buttons(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"type": "object",
"description": "A list of action-buttons to be displayed in the topbar",
"additionalProperties": false,
"properties": {
"position": {
"type": "string",
"description": "Horizontal position of the button in the bar",
"default": "right",
"enum": ["right", "left"]
},
"actions": {
"$ref": "#/widgets/buttons-grid/properties/actions"
}
}
},
"^power-profiles(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
"type": "object",
"description": "A button that opens a dropdown with power profile options from D-Bus PowerProfiles daemon. Profiles are detected automatically.",
"additionalProperties": false,
"properties": {
"label": {
"type": "string",
"description": "Text to be displayed in the button",
"default": "Power"
},
"position": {
"type": "string",
"description": "Horizontal position of the button in the bar",
"default": "right",
"enum": ["right", "left"]
},
"animation-type": {
"type": "string",
"default": "slide_down",
"description": "Animation type for the dropdown",
"enum": ["slide_down", "slide_up", "none"]
},
"animation-duration": {
"type": "integer",
"default": 250,
"description": "Duration of animation in milliseconds"
},
"button-labels": {
"type": "object",
"description": "Override button text for specific profiles. Keys are profile names (performance, balanced, power-saver), values are the full button text.",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"slider": { "slider": {
"type": "object", "type": "object",
"description": "general slider control", "description": "general slider control",
......
...@@ -11,20 +11,6 @@ namespace XimperShellNotificationCenter.Widgets { ...@@ -11,20 +11,6 @@ namespace XimperShellNotificationCenter.Widgets {
public string key { get; private set; } public string key { get; private set; }
public string suffix { get; private set; } public string suffix { get; private set; }
public enum ButtonType {
TOGGLE,
NORMAL;
public static ButtonType parse (string value) {
switch (value) {
case "toggle":
return ButtonType.TOGGLE;
default:
return ButtonType.NORMAL;
}
}
}
protected BaseWidget (string suffix) { protected BaseWidget (string suffix) {
this.suffix = suffix; this.suffix = suffix;
this.key = widget_name + (suffix.length > 0 ? "#%s".printf (suffix) : ""); this.key = widget_name + (suffix.length > 0 ? "#%s".printf (suffix) : "");
...@@ -102,37 +88,5 @@ namespace XimperShellNotificationCenter.Widgets { ...@@ -102,37 +88,5 @@ namespace XimperShellNotificationCenter.Widgets {
return config.get_array_member (value_key); return config.get_array_member (value_key);
} }
protected Action[] parse_actions (Json.Array actions) {
Action[] res = new Action[actions.get_length ()];
for (int i = 0; i < actions.get_length (); i++) {
string label =
actions.get_object_element (i).get_string_member_with_default ("label",
"label");
string command =
actions.get_object_element (i).get_string_member_with_default ("command", "");
string t = actions.get_object_element (i).get_string_member_with_default ("type",
"normal");
ButtonType type = ButtonType.parse (t);
string update_command =
actions.get_object_element (i).get_string_member_with_default ("update-command",
"");
bool active =
actions.get_object_element (i).get_boolean_member_with_default ("active",
false);
res[i] = Action () {
label = label,
command = command,
type = type,
update_command = update_command,
active = active
};
}
return res;
}
protected async void execute_command (string cmd, string[] env_additions = {}) {
string msg = "";
yield Functions.execute_command (cmd, env_additions, out msg);
}
} }
} }
using GLib;
namespace XimperShellNotificationCenter.Widgets {
public class ButtonsGrid : BaseWidget {
public override string widget_name {
get {
return "buttons-grid";
}
}
Action[] actions;
// 7 is the default Gtk.FlowBox.max_children_per_line
int buttons_per_row = 7;
List<ToggleButton> toggle_buttons;
public ButtonsGrid (string suffix) {
base (suffix);
Json.Object ?config = get_config (this);
if (config != null) {
Json.Array a = get_prop_array (config, "actions");
if (a != null) {
actions = parse_actions (a);
}
bool bpr_found = false;
int bpr = get_prop<int> (config, "buttons-per-row", out bpr_found);
if (bpr_found) {
buttons_per_row = bpr;
}
}
Gtk.FlowBox container = new Gtk.FlowBox ();
container.set_max_children_per_line (buttons_per_row);
container.set_selection_mode (Gtk.SelectionMode.NONE);
container.set_hexpand (true);
append (container);
// add action to container
foreach (var act in actions) {
switch (act.type) {
case ButtonType.TOGGLE :
ToggleButton tb = new ToggleButton (act.label, act.command,
act.update_command, act.active);
container.insert (tb, -1);
toggle_buttons.append (tb);
break;
default:
Gtk.Button b = new Gtk.Button.with_label (act.label);
b.clicked.connect (() => execute_command.begin (act.command));
container.insert (b, -1);
break;
}
}
}
public override void on_cc_visibility_change (bool value) {
if (value) {
foreach (var tb in toggle_buttons) {
tb.on_update.begin ();
}
}
}
}
}
namespace XimperShellNotificationCenter.Widgets {
public class Dnd : BaseWidget {
public override string widget_name {
get {
return "dnd";
}
}
Gtk.Label title_widget;
Gtk.Switch dnd_button;
// Default config values
string title;
public Dnd (string suffix) {
base (suffix);
title = _("Do Not Disturb");
Json.Object ?config = get_config (this);
if (config != null) {
// Get title
string ?title = get_prop<string> (config, "text");
if (title != null) {
this.title = title;
}
}
// Title
title_widget = new Gtk.Label (title);
title_widget.set_hexpand (true);
title_widget.set_halign (Gtk.Align.START);
append (title_widget);
// Dnd button
dnd_button = new Gtk.Switch () {
active = noti_daemon.dnd,
};
dnd_button.notify["active"].connect (switch_active_changed_cb);
noti_daemon.on_dnd_toggle.connect ((dnd) => {
dnd_button.notify["active"].disconnect (switch_active_changed_cb);
dnd_button.set_active (dnd);
dnd_button.notify["active"].connect (switch_active_changed_cb);
});
dnd_button.set_can_focus (false);
dnd_button.valign = Gtk.Align.CENTER;
// Backwards compatible towards older CSS stylesheets
dnd_button.add_css_class ("control-center-dnd");
append (dnd_button);
}
private void switch_active_changed_cb () {
noti_daemon.dnd = dnd_button.active;
}
}
}
...@@ -19,21 +19,12 @@ namespace XimperShellNotificationCenter.Widgets { ...@@ -19,21 +19,12 @@ namespace XimperShellNotificationCenter.Widgets {
case "title": case "title":
widget = new Title (suffix); widget = new Title (suffix);
break; break;
case "dnd":
widget = new Dnd (suffix);
break;
case "label": case "label":
widget = new Label (suffix); widget = new Label (suffix);
break; break;
case "mpris": case "mpris":
widget = new Mpris.Mpris (suffix); widget = new Mpris.Mpris (suffix);
break; break;
case "menubar":
widget = new Menubar (suffix);
break;
case "buttons-grid":
widget = new ButtonsGrid (suffix);
break;
case "quick-settings": case "quick-settings":
widget = new QuickSettings (suffix); widget = new QuickSettings (suffix);
break; break;
......
using GLib;
namespace XimperShellNotificationCenter.Widgets {
public enum MenuType {
BUTTONS,
MENU,
POWER_PROFILES
}
public enum Position {
LEFT,
RIGHT
}
public struct ConfigObject {
string ?name;
MenuType ?type;
string ?label;
Position ?position;
Action[] actions;
Gtk.Revealer ?revealer;
int animation_duration;
Gtk.RevealerTransitionType animation_type;
HashTable<string, string> ?label_overrides;
}
public struct Action {
string ?label;
string ?command;
BaseWidget.ButtonType ?type;
string ?update_command;
bool ?active;
}
public class Menubar : BaseWidget {
public override string widget_name {
get {
return "menubar";
}
}
Gtk.Box left_container;
Gtk.Box right_container;
List<ConfigObject ?> menu_objects;
List<ToggleButton> toggle_buttons;
List<PowerProfiles> power_profiles_widgets;
public Menubar (string suffix) {
base (suffix);
set_orientation (Gtk.Orientation.VERTICAL);
set_hexpand (true);
power_profiles_widgets = new List<PowerProfiles> ();
Json.Object ?config = get_config (this);
if (config != null) {
parse_config_objects (config);
}
Gtk.Box topbar_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
topbar_container.add_css_class ("menu-button-bar");
append (topbar_container);
left_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
overflow = Gtk.Overflow.HIDDEN,
hexpand = true,
halign = Gtk.Align.START,
};
left_container.add_css_class ("widget-menubar-container");
left_container.add_css_class ("start");
topbar_container.append (left_container);
right_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
overflow = Gtk.Overflow.HIDDEN,
hexpand = true,
halign = Gtk.Align.END,
};
right_container.add_css_class ("widget-menubar-container");
right_container.add_css_class ("end");
topbar_container.append (right_container);
for (int i = 0; i < menu_objects.length (); i++) {
unowned ConfigObject ?obj = menu_objects.nth_data (i);
add_menu (ref obj);
}
foreach (var obj in menu_objects) {
obj.revealer ?.set_reveal_child (false);
}
}
void add_menu (ref unowned ConfigObject ?obj) {
switch (obj.type) {
case MenuType.BUTTONS :
Gtk.Box container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
if (obj.name != null) {
container.add_css_class (obj.name);
}
foreach (Action a in obj.actions) {
switch (a.type) {
case ButtonType.TOGGLE :
ToggleButton tb = new ToggleButton (a.label, a.command,
a.update_command, a.active);
container.append (tb);
toggle_buttons.append (tb);
break;
default :
Gtk.Button b = new Gtk.Button.with_label (a.label);
b.clicked.connect (() => execute_command.begin (a.command));
container.append (b);
break;
}
}
switch (obj.position) {
case Position.LEFT :
left_container.append (container);
break;
case Position.RIGHT :
right_container.append (container);
break;
}
break;
case MenuType.MENU :
Gtk.ToggleButton show_button = new Gtk.ToggleButton.with_label (obj.label);
Gtk.Box menu = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
if (obj.name != null) {
menu.add_css_class (obj.name);
}
Gtk.Revealer r = new Gtk.Revealer ();
r.set_child (menu);
r.set_transition_duration (obj.animation_duration);
r.set_transition_type (obj.animation_type);
obj.revealer = r;
// Make sure that the toggle buttons state is always synced
// with the revealers visibility.
r.bind_property ("child-revealed",
show_button, "active", BindingFlags.SYNC_CREATE, null, null);
show_button.clicked.connect (() => {
bool visible = !r.get_reveal_child ();
foreach (var o in menu_objects) {
o.revealer ?.set_reveal_child (false);
}
foreach (var pp in power_profiles_widgets) {
pp.close ();
}
r.set_reveal_child (visible);
});
foreach (var a in obj.actions) {
switch (a.type) {
case ButtonType.TOGGLE :
ToggleButton tb = new ToggleButton (a.label, a.command,
a.update_command, a.active);
tb.set_hexpand (true);
menu.append (tb);
toggle_buttons.append (tb);
break;
default :
Gtk.Button b = new Gtk.Button.with_label (a.label);
b.set_hexpand (true);
b.clicked.connect (() => execute_command.begin (a.command));
menu.append (b);
break;
}
}
switch (obj.position) {
case Position.RIGHT:
right_container.append (show_button);
break;
case Position.LEFT:
left_container.append (show_button);
break;
}
append (r);
break;
case MenuType.POWER_PROFILES :
var pp = new PowerProfiles (
obj.label,
obj.animation_duration,
obj.animation_type,
obj.label_overrides);
pp.request_close_other_revealers.connect (() => {
foreach (var o in menu_objects) {
o.revealer ?.set_reveal_child (false);
}
foreach (var other_pp in power_profiles_widgets) {
if (other_pp != pp) {
other_pp.close ();
}
}
});
obj.revealer = pp.get_revealer ();
power_profiles_widgets.append (pp);
switch (obj.position) {
case Position.RIGHT:
right_container.append (pp.get_button ());
break;
case Position.LEFT:
left_container.append (pp.get_button ());
break;
}
append (pp.get_revealer ());
break;
}
}
protected void parse_config_objects (Json.Object config) {
var elements = config.get_members ();
menu_objects = new List<ConfigObject ?> ();
for (int i = 0; i < elements.length (); i++) {
string e = elements.nth_data (i);
Json.Object ?obj = config.get_object_member (e);
if (obj == null) {
continue;
}
string[] key = e.split ("#");
string t = key[0];
MenuType type = MenuType.BUTTONS;
if (t == "buttons") {
type = MenuType.BUTTONS;
} else if (t == "menu") {
type = MenuType.MENU;
} else if (t == "power-profiles") {
type = MenuType.POWER_PROFILES;
} else {
info ("Invalid type for menu-object - valid options: " +
"'menu' || 'buttons' || 'power-profiles' using default");
}
string name = key[1];
string ?p = get_prop<string> (obj, "position");
Position pos;
if (p != "left" && p != "right") {
pos = Position.RIGHT;
info ("No position for menu-object given using default");
} else if (p == "right") {
pos = Position.RIGHT;
} else {
pos = Position.LEFT;
}
Json.Array ?actions = get_prop_array (obj, "actions");
if (actions == null && type != MenuType.POWER_PROFILES) {
info ("Error parsing actions for menu-object");
}
string ?label = get_prop<string> (obj, "label");
if (label == null) {
label = _("Menu");
info ("No label for menu-object given using default");
}
int duration = int.max (0, get_prop<int> (obj, "animation-duration"));
if (duration == 0) {
duration = 250;
}
string ?animation_type = get_prop<string> (obj, "animation-type");
if (animation_type == null) {
animation_type = "slide_down";
info ("No animation-type for menu-object given using default");
}
Gtk.RevealerTransitionType revealer_type;
switch (animation_type) {
default :
case "none" :
revealer_type = Gtk.RevealerTransitionType.NONE;
break;
case "slide_up":
revealer_type = Gtk.RevealerTransitionType.SLIDE_UP;
break;
case "slide_down":
revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
break;
}
Action[] actions_list = actions != null ? parse_actions (actions) : new Action[0];
// Parse label overrides for power-profiles
HashTable<string, string>? icons = null;
if (type == MenuType.POWER_PROFILES && obj.has_member ("button-labels")) {
icons = new HashTable<string, string> (str_hash, str_equal);
var icons_obj = obj.get_object_member ("button-labels");
if (icons_obj != null) {
foreach (string profile_key in icons_obj.get_members ()) {
icons.insert (profile_key, icons_obj.get_string_member (profile_key));
}
}
}
menu_objects.append (ConfigObject () {
name = name,
type = type,
label = label,
position = pos,
actions = actions_list,
revealer = null,
animation_duration = duration,
animation_type = revealer_type,
label_overrides = icons,
});
}
}
public override void on_cc_visibility_change (bool val) {
if (!val) {
foreach (var obj in menu_objects) {
obj.revealer ?.set_reveal_child (false);
}
foreach (var pp in power_profiles_widgets) {
pp.close ();
}
} else {
foreach (var tb in toggle_buttons) {
tb.on_update.begin ();
}
}
}
}
}
namespace XimperShellNotificationCenter.Widgets {
[DBus (name = "net.hadess.PowerProfiles")]
interface PowerProfilesProxy : Object {
public abstract string active_profile { owned get; set; }
public abstract HashTable<string, Variant>[] profiles { owned get; }
}
public class PowerProfiles : Object {
PowerProfilesProxy? proxy = null;
DBusProxy raw_proxy = null;
Gtk.ToggleButton show_button;
Gtk.Revealer revealer;
Gtk.Box menu;
string[] profile_names = {};
Gtk.ToggleButton[] profile_buttons = {};
bool updating_buttons = false;
// Custom label overrides from config (profile_name -> full button text)
HashTable<string, string> label_overrides;
public signal void request_close_other_revealers ();
public PowerProfiles (string label,
int animation_duration,
Gtk.RevealerTransitionType animation_type,
HashTable<string, string>? label_overrides) {
this.label_overrides = label_overrides ?? new HashTable<string, string> (str_hash, str_equal);
show_button = new Gtk.ToggleButton.with_label (label);
menu = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
menu.add_css_class ("power-profiles");
revealer = new Gtk.Revealer () {
child = menu,
transition_duration = animation_duration,
transition_type = animation_type,
};
revealer.bind_property ("child-revealed",
show_button, "active",
BindingFlags.SYNC_CREATE, null, null);
show_button.clicked.connect (() => {
bool visible = !revealer.get_reveal_child ();
request_close_other_revealers ();
revealer.set_reveal_child (visible);
});
connect_dbus.begin ();
}
private async void connect_dbus () {
try {
proxy = yield Bus.get_proxy (BusType.SYSTEM,
"net.hadess.PowerProfiles",
"/net/hadess/PowerProfiles");
raw_proxy = proxy as DBusProxy;
populate_profiles ();
raw_proxy.g_properties_changed.connect (on_properties_changed);
} catch (Error e) {
warning ("PowerProfiles D-Bus not available: %s", e.message);
show_button.set_visible (false);
revealer.set_visible (false);
}
}
private void on_properties_changed (Variant changed, string[] invalidated) {
var iter = changed.iterator ();
string key;
Variant val;
while (iter.next ("{sv}", out key, out val)) {
if (key == "ActiveProfile") {
update_active_button (val.get_string ());
} else if (key == "Profiles") {
populate_profiles ();
}
}
}
private async void set_profile (string profile_name) {
if (raw_proxy == null) return;
try {
yield raw_proxy.call (
"org.freedesktop.DBus.Properties.Set",
new Variant ("(ssv)",
"net.hadess.PowerProfiles",
"ActiveProfile",
new Variant.string (profile_name)),
DBusCallFlags.NONE, -1, null);
} catch (Error e) {
warning ("Failed to set power profile: %s", e.message);
}
}
private void populate_profiles () {
if (proxy == null) return;
Gtk.Widget? child = menu.get_first_child ();
while (child != null) {
Gtk.Widget? next = child.get_next_sibling ();
menu.remove (child);
child = next;
}
profile_names = {};
profile_buttons = {};
string active = proxy.active_profile;
foreach (var profile in proxy.profiles) {
Variant? v = profile.lookup ("Profile");
if (v == null) continue;
string name = v.get_string ();
// Use override if set, otherwise default
string? custom = label_overrides.lookup (name);
string display = custom ?? get_default_label (name);
var btn = new Gtk.ToggleButton ();
btn.set_hexpand (true);
btn.set_label (display);
btn.set_active (name == active);
string profile_name = name;
btn.toggled.connect (() => {
if (!updating_buttons && btn.active) {
set_profile.begin (profile_name);
}
});
menu.append (btn);
profile_names += name;
profile_buttons += btn;
}
}
private void update_active_button (string active_profile) {
updating_buttons = true;
for (int i = 0; i < profile_names.length; i++) {
profile_buttons[i].set_active (profile_names[i] == active_profile);
}
updating_buttons = false;
}
private string get_default_label (string profile) {
switch (profile) {
case "performance": return _("Performance");
case "balanced": return _("Balanced");
case "power-saver": return _("Power Saver");
default: return profile;
}
}
public Gtk.ToggleButton get_button () { return show_button; }
public Gtk.Revealer get_revealer () { return revealer; }
public void close () { revealer.set_reveal_child (false); }
}
}
namespace XimperShellNotificationCenter.Widgets {
class ToggleButton : Gtk.ToggleButton {
private string command;
private string update_command;
private ulong handler_id;
public ToggleButton (string label, string command, string update_command, bool active) {
this.command = command;
this.update_command = update_command;
this.label = label;
this.set_has_frame (true);
if (active) {
this.active = true;
}
this.handler_id = this.toggled.connect (on_toggle);
}
private async void on_toggle () {
string msg = "";
string[] env_additions = { "XSNC_TOGGLE_STATE=" + this.active.to_string () };
yield Functions.execute_command (this.command, env_additions, out msg);
}
public async void on_update () {
if (update_command == "") {
return;
}
string msg = "";
string[] env_additions = { "XSNC_TOGGLE_STATE=" + this.active.to_string () };
yield Functions.execute_command (this.update_command, env_additions, out msg);
try {
// remove trailing whitespaces
Regex regex = new Regex ("\\s+$");
string res = regex.replace (msg, msg.length, 0, "");
GLib.SignalHandler.block (this, this.handler_id);
if (res.up () == "TRUE") {
this.active = true;
} else {
this.active = false;
}
GLib.SignalHandler.unblock (this, this.handler_id);
} catch (RegexError e) {
stderr.printf ("RegexError: %s\n", e.message);
}
}
}
}
...@@ -28,23 +28,16 @@ widget_sources = [ ...@@ -28,23 +28,16 @@ widget_sources = [
# Helpers # Helpers
'controlCenter/widgets/baseWidget.vala', 'controlCenter/widgets/baseWidget.vala',
'controlCenter/widgets/factory.vala', 'controlCenter/widgets/factory.vala',
'controlCenter/widgets/shared/toggleButton.vala',
# Widget: Notifications # Widget: Notifications
'controlCenter/widgets/notifications/notifications.vala', 'controlCenter/widgets/notifications/notifications.vala',
# Widget: Title # Widget: Title
'controlCenter/widgets/title/title.vala', 'controlCenter/widgets/title/title.vala',
# Widget: Dnd
'controlCenter/widgets/dnd/dnd.vala',
# Widget: Label # Widget: Label
'controlCenter/widgets/label/label.vala', 'controlCenter/widgets/label/label.vala',
# Widget: MPRIS # Widget: MPRIS
'controlCenter/widgets/mpris/mpris.vala', 'controlCenter/widgets/mpris/mpris.vala',
'controlCenter/widgets/mpris/interfaces.vala', 'controlCenter/widgets/mpris/interfaces.vala',
'controlCenter/widgets/mpris/mpris_player.vala', 'controlCenter/widgets/mpris/mpris_player.vala',
# Widget: Menubar
'controlCenter/widgets/menubar/menubar.vala',
# Widget: Buttons Grid
'controlCenter/widgets/buttonsGrid/buttonsGrid.vala',
# Widget: Slider # Widget: Slider
'controlCenter/widgets/slider/slider.vala', 'controlCenter/widgets/slider/slider.vala',
# Widget: Backlight Slider # Widget: Backlight Slider
...@@ -52,8 +45,6 @@ widget_sources = [ ...@@ -52,8 +45,6 @@ widget_sources = [
'controlCenter/widgets/backlight/backlightUtil.vala', 'controlCenter/widgets/backlight/backlightUtil.vala',
# Widget: Inhibitors # Widget: Inhibitors
'controlCenter/widgets/inhibitors/inhibitors.vala', 'controlCenter/widgets/inhibitors/inhibitors.vala',
# Widget: Power Profiles (D-Bus PPD integration for menubar)
'controlCenter/widgets/powerProfiles/powerProfiles.vala',
# Widget: Quick Settings # Widget: Quick Settings
'controlCenter/widgets/quickSettings/quickSettings.vala', 'controlCenter/widgets/quickSettings/quickSettings.vala',
'controlCenter/widgets/quickSettings/quickSettingsTile.vala', 'controlCenter/widgets/quickSettings/quickSettingsTile.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