status/backlight: Add new quick toggle

Settings no longer exposes a slider for the keyboard brightness,
leaving keyboard shortcuts as the only way of adjusting it.

This is good enough in most cases, because devices with keyboard
backlight usually include corresponding keys on their keyboard.

However for devices without those keys, it would be good for the
settings to be exposed somewhere again. Quick settings seems like
a more appropriate place than "proper" Settings, since it gives
quick access that doesn't require a major focus change.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6765

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2820>
This commit is contained in:
Florian Müllner 2023-06-13 21:36:20 +02:00 committed by Marge Bot
parent 797df4f52f
commit 62db8dc16e
11 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,7 @@
<node>
<interface name='org.gnome.SettingsDaemon.Power.Keyboard'>
<property name='Brightness' type='i' access='readwrite'/>
<property name='Steps' type='i' access='read'/>
</interface>
</node>

View File

@ -37,6 +37,7 @@
<file preprocess="xml-stripblanks">org.gnome.SessionManager.Presence.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SessionManager.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Color.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Power.Keyboard.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Power.Screen.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Rfkill.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Wacom.xml</file>

View File

@ -22,6 +22,9 @@
<file>scalable/actions/screenshot-ui-window-symbolic.svg</file>
<file>scalable/actions/screenshot-recorded-symbolic.svg</file>
<file>scalable/status/background-app-ghost-symbolic.svg</file>
<file>scalable/status/keyboard-brightness-high-symbolic.svg</file>
<file>scalable/status/keyboard-brightness-low-symbolic.svg</file>
<file>scalable/status/keyboard-brightness-medium-symbolic.svg</file>
<file>scalable/status/keyboard-caps-lock-symbolic.svg</file>
<file>scalable/status/keyboard-enter-symbolic.svg</file>
<file>scalable/status/keyboard-hide-symbolic.svg</file>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 3 c -0.554688 0 -1 0.445312 -1 1 v 1 c 0 0.554688 0.445312 1 1 1 s 1 -0.445312 1 -1 v -1 c 0 -0.554688 -0.445312 -1 -1 -1 z m -5 2.050781 c -0.238281 0.007813 -0.476562 0.105469 -0.660156 0.292969 c -0.390625 0.390625 -0.390625 1.019531 0 1.414062 l 0.71875 0.71875 c 0.390625 0.390626 1.023437 0.390626 1.414062 0 c 0.390625 -0.394531 0.390625 -1.023437 0 -1.414062 l -0.71875 -0.71875 c -0.207031 -0.210938 -0.484375 -0.308594 -0.753906 -0.292969 z m 9.949219 0 c -0.253907 0 -0.511719 0.097657 -0.707031 0.292969 l -0.71875 0.71875 c -0.390626 0.390625 -0.390626 1.023438 0 1.414062 c 0.390624 0.390626 1.023437 0.390626 1.414062 0 l 0.71875 -0.71875 c 0.390625 -0.390624 0.390625 -1.023437 0 -1.414062 c -0.195312 -0.195312 -0.453125 -0.292969 -0.707031 -0.292969 z m -11.949219 4.949219 c -0.554688 0 -1 0.445312 -1 1 s 0.445312 1 1 1 h 0.972656 c 0.554688 0 1 -0.445312 1 -1 s -0.445312 -1 -1 -1 z m 12.980469 0 c -0.550781 0 -1 0.445312 -1 1 s 0.449219 1 1 1 h 1.019531 c 0.554688 0 1 -0.445312 1 -1 s -0.445312 -1 -1 -1 z m 0 0"/>
<path d="m 5.5 10 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1.5 h 6 v -1.5 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 h -0.5 s -1.332031 0 -2 0 s -2 0 -2 0 z m 0 0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 4 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m -4.398438 1.601562 c -0.554687 0 -1 0.445313 -1 1 c 0 0.550782 0.445313 1 1 1 c 0.550782 0 1 -0.449218 1 -1 c 0 -0.554687 -0.449218 -1 -1 -1 z m 9 0 c -0.554687 0 -1 0.445313 -1 1 c 0 0.550782 0.445313 1 1 1 c 0.550782 0 1 -0.449218 1 -1 c 0 -0.554687 -0.449218 -1 -1 -1 z m -10.601562 4.398438 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 12 0 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0" fill-opacity="0.34902"/>
<path d="m 5.5 10 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1.5 h 6 v -1.5 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 h -0.5 s -1.332031 0 -2 0 s -2 0 -2 0 z m 0 0"/>
</svg>

After

Width:  |  Height:  |  Size: 923 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 4 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m -4.398438 1.601562 c -0.554687 0 -1 0.445313 -1 1 c 0 0.550782 0.445313 1 1 1 c 0.550782 0 1 -0.449218 1 -1 c 0 -0.554687 -0.449218 -1 -1 -1 z m 9 0 c -0.554687 0 -1 0.445313 -1 1 c 0 0.550782 0.445313 1 1 1 c 0.550782 0 1 -0.449218 1 -1 c 0 -0.554687 -0.449218 -1 -1 -1 z m -10.601562 4.398438 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 12 0 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 s 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/>
<path d="m 5.5 10 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1.5 h 6 v -1.5 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 h -0.5 s -1.332031 0 -2 0 s -2 0 -2 0 z m 0 0"/>
</svg>

After

Width:  |  Height:  |  Size: 900 B

View File

@ -163,6 +163,16 @@
.device-subtitle { color: transparentize($fg_color, 0.5); }
.keyboard-brightness-item .slider {
min-height: 16px;
}
.keyboard-brightness-level {
spacing: $base_padding;
.button:checked { @include button(default, $c:$selected_bg_color); }
}
// background apps
.background-apps-quick-toggle {

View File

@ -137,6 +137,7 @@
<file>ui/status/accessibility.js</file>
<file>ui/status/autoRotate.js</file>
<file>ui/status/backgroundApps.js</file>
<file>ui/status/backlight.js</file>
<file>ui/status/brightness.js</file>
<file>ui/status/camera.js</file>
<file>ui/status/darkMode.js</file>

View File

@ -30,6 +30,7 @@ const SystemStatus = imports.ui.status.system;
const LocationStatus = imports.ui.status.location;
const NightLightStatus = imports.ui.status.nightLight;
const DarkModeStatus = imports.ui.status.darkMode;
const BacklightStatus = imports.ui.status.backlight;
const ThunderboltStatus = imports.ui.status.thunderbolt;
const AutoRotateStatus = imports.ui.status.autoRotate;
const BackgroundAppsStatus = imports.ui.status.backgroundApps;
@ -398,6 +399,7 @@ class QuickSettings extends PanelMenu.Button {
this._thunderbolt = new ThunderboltStatus.Indicator();
this._nightLight = new NightLightStatus.Indicator();
this._darkMode = new DarkModeStatus.Indicator();
this._backlight = new BacklightStatus.Indicator();
this._powerProfiles = new PowerProfileStatus.Indicator();
this._rfkill = new RFKillStatus.Indicator();
this._autoRotate = new AutoRotateStatus.Indicator();
@ -414,6 +416,7 @@ class QuickSettings extends PanelMenu.Button {
if (this._network)
this._indicators.add_child(this._network);
this._indicators.add_child(this._darkMode);
this._indicators.add_child(this._backlight);
this._indicators.add_child(this._powerProfiles);
if (this._bluetooth)
this._indicators.add_child(this._bluetooth);
@ -439,6 +442,7 @@ class QuickSettings extends PanelMenu.Button {
this._addItems(this._powerProfiles.quickSettingsItems);
this._addItems(this._nightLight.quickSettingsItems);
this._addItems(this._darkMode.quickSettingsItems);
this._addItems(this._backlight.quickSettingsItems);
this._addItems(this._rfkill.quickSettingsItems);
this._addItems(this._autoRotate.quickSettingsItems);
this._addItems(this._unsafeMode.quickSettingsItems);

205
js/ui/status/backlight.js Normal file
View File

@ -0,0 +1,205 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
const {Clutter, Gio, GObject, St} = imports.gi;
const {QuickMenuToggle, SystemIndicator} = imports.ui.quickSettings;
const PopupMenu = imports.ui.popupMenu;
const {Slider} = imports.ui.slider;
const {loadInterfaceXML} = imports.misc.fileUtils;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Power.Keyboard');
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
const SliderItem = GObject.registerClass({
Properties: {
'value': GObject.ParamSpec.int(
'value', '', '',
GObject.ParamFlags.READWRITE,
0, 100, 0),
},
}, class SliderItem extends PopupMenu.PopupBaseMenuItem {
constructor() {
super({
activate: false,
style_class: 'keyboard-brightness-item',
});
this._slider = new Slider(0);
this._sliderChangedId = this._slider.connect('notify::value',
() => this.notify('value'));
this._slider.accessible_name = _('Keyboard Brightness');
this.add_child(this._slider);
}
get value() {
return this._slider.value * 100;
}
set value(value) {
this._slider.block_signal_handler(this._sliderChangedId);
this._slider.value = value / 100;
this._slider.unblock_signal_handler(this._sliderChangedId);
}
});
const DiscreteItem = GObject.registerClass({
Properties: {
'value': GObject.ParamSpec.int(
'value', '', '',
GObject.ParamFlags.READWRITE,
0, 100, 0),
'n-levels': GObject.ParamSpec.int(
'n-levels', '', '',
GObject.ParamFlags.READWRITE,
1, 3, 1),
},
}, class DiscreteItem extends St.BoxLayout {
constructor() {
super({
style_class: 'popup-menu-item',
reactive: true,
});
this._levelButtons = new Map();
this._addLevelButton('off', _('Off'), 'keyboard-brightness-low-symbolic');
this._addLevelButton('low', _('Low'), 'keyboard-brightness-medium-symbolic');
this._addLevelButton('high', _('High'), 'keyboard-brightness-high-symbolic');
this.connect('notify::n-levels', () => this._syncLevels());
this.connect('notify::value', () => this._syncChecked());
this._syncLevels();
}
_valueToLevel(value) {
const checkedIndex = Math.round(value * (this.nLevels - 1) / 100);
if (checkedIndex === this.nLevels - 1)
return 'high';
return [...this._levelButtons.keys()].at(checkedIndex);
}
_levelToValue(level) {
const keyIndex = [...this._levelButtons.keys()].indexOf(level);
return 100 * Math.min(keyIndex, this.nLevels - 1) / (this.nLevels - 1);
}
_addLevelButton(key, label, iconName) {
const box = new St.BoxLayout({
style_class: 'keyboard-brightness-level',
vertical: true,
x_expand: true,
});
box.button = new St.Button({
styleClass: 'icon-button',
canFocus: true,
iconName,
});
box.add_child(box.button);
box.button.connect('clicked', () => {
this.value = this._levelToValue(key);
});
box.add_child(new St.Label({
text: label,
x_align: Clutter.ActorAlign.CENTER,
}));
this.add_child(box);
this._levelButtons.set(key, box);
}
_syncLevels() {
this._levelButtons.get('off').visible = this.nLevels > 0;
this._levelButtons.get('high').visible = this.nLevels > 1;
this._levelButtons.get('low').visible = this.nLevels > 2;
}
_syncChecked() {
const checkedKey = this._valueToLevel(this.value);
this._levelButtons.forEach((b, k) => {
b.button.checked = k === checkedKey;
});
}
});
const KeyboardBrightnessToggle = GObject.registerClass(
class KeyboardBrightnessToggle extends QuickMenuToggle {
_init() {
super._init({
title: _('Keyboard'),
iconName: 'display-brightness-symbolic',
});
this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
(proxy, error) => {
if (error)
console.error(error.message);
else
this._proxy.connect('g-properties-changed', () => this._sync());
this._sync();
});
this.connect('clicked', () => {
this._proxy.Brightness = this.checked ? 0 : 100;
});
this._sliderItem = new SliderItem();
this.menu.box.add_child(this._sliderItem);
this._discreteItem = new DiscreteItem();
this.menu.box.add_child(this._discreteItem);
this._sliderItem.bind_property('visible',
this._discreteItem, 'visible',
GObject.BindingFlags.INVERT_BOOLEAN |
GObject.BindingFlags.SYNC_CREATE);
this._sliderItem.bind_property('value',
this._discreteItem, 'value',
GObject.BindingFlags.SYNC_CREATE);
this._sliderItem.connect('notify::value',
() => (this._proxy.Brightness = this._sliderItem.value));
this._discreteItem.connect('notify::value',
() => (this._proxy.Brightness = this._discreteItem.value));
}
_sync() {
const brightness = this._proxy.Brightness;
const visible = Number.isInteger(brightness) && brightness >= 0;
this.visible = visible;
if (!visible)
return;
this.checked = brightness > 0;
const useSlider = this._proxy.Steps >= 4;
this._sliderItem.set({
visible: useSlider,
value: brightness,
});
if (!useSlider)
this._discreteItem.nLevels = this._proxy.Steps;
}
});
var Indicator = GObject.registerClass(
class Indicator extends SystemIndicator {
_init() {
super._init();
this.quickSettingsItems.push(new KeyboardBrightnessToggle());
}
});

View File

@ -60,6 +60,7 @@ js/ui/shellMountOperation.js
js/ui/status/accessibility.js
js/ui/status/autoRotate.js
js/ui/status/backgroundApps.js
js/ui/status/backlight.js
js/ui/status/bluetooth.js
js/ui/status/brightness.js
js/ui/status/darkMode.js