gnome-shell/js/ui/panelMenu.js
Florian Müllner a42f7c2384 cleanup: Use type-safe comparisons
We have been using type-safe comparisons in new code for quite a while
now, however old code has only been adapted slowly.

Change all the remaining bits to get rid of another legacy style
difference.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2866>
2023-08-09 15:10:38 +00:00

236 lines
7.0 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
import Atk from 'gi://Atk';
import Clutter from 'gi://Clutter';
import GObject from 'gi://GObject';
import St from 'gi://St';
import * as Main from './main.js';
import * as Params from '../misc/params.js';
import * as PopupMenu from './popupMenu.js';
export const ButtonBox = GObject.registerClass(
class ButtonBox extends St.Widget {
_init(params) {
params = Params.parse(params, {
style_class: 'panel-button',
x_expand: true,
y_expand: true,
}, true);
super._init(params);
this._delegate = this;
this.container = new St.Bin({child: this});
this.connect('style-changed', this._onStyleChanged.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
this._minHPadding = this._natHPadding = 0.0;
}
_onStyleChanged(actor) {
let themeNode = actor.get_theme_node();
this._minHPadding = themeNode.get_length('-minimum-hpadding');
this._natHPadding = themeNode.get_length('-natural-hpadding');
}
vfunc_get_preferred_width(_forHeight) {
let child = this.get_first_child();
let minimumSize, naturalSize;
if (child)
[minimumSize, naturalSize] = child.get_preferred_width(-1);
else
minimumSize = naturalSize = 0;
minimumSize += 2 * this._minHPadding;
naturalSize += 2 * this._natHPadding;
return [minimumSize, naturalSize];
}
vfunc_get_preferred_height(_forWidth) {
let child = this.get_first_child();
if (child)
return child.get_preferred_height(-1);
return [0, 0];
}
vfunc_allocate(box) {
this.set_allocation(box);
let child = this.get_first_child();
if (!child)
return;
let [, natWidth] = child.get_preferred_width(-1);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let childBox = new Clutter.ActorBox();
if (natWidth + 2 * this._natHPadding <= availWidth) {
childBox.x1 = this._natHPadding;
childBox.x2 = availWidth - this._natHPadding;
} else {
childBox.x1 = this._minHPadding;
childBox.x2 = availWidth - this._minHPadding;
}
childBox.y1 = 0;
childBox.y2 = availHeight;
child.allocate(childBox);
}
_onDestroy() {
this.container.child = null;
this.container.destroy();
}
});
export const Button = GObject.registerClass({
Signals: {'menu-set': {}},
}, class PanelMenuButton extends ButtonBox {
_init(menuAlignment, nameText, dontCreateMenu) {
super._init({
reactive: true,
can_focus: true,
track_hover: true,
accessible_name: nameText ?? '',
accessible_role: Atk.Role.MENU,
});
if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this);
else
this.setMenu(new PopupMenu.PopupMenu(this, menuAlignment, St.Side.TOP, 0));
this.connect('key-press-event',
(o, ev) => global.focus_manager.navigate_from_event(ev));
}
setSensitive(sensitive) {
this.reactive = sensitive;
this.can_focus = sensitive;
this.track_hover = sensitive;
}
setMenu(menu) {
if (this.menu)
this.menu.destroy();
this.menu = menu;
if (this.menu) {
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this));
this.menu.actor.connect('key-press-event', this._onMenuKeyPress.bind(this));
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
}
this.emit('menu-set');
}
vfunc_event(event) {
if (this.menu &&
(event.type() === Clutter.EventType.TOUCH_BEGIN ||
event.type() === Clutter.EventType.BUTTON_PRESS))
this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
}
vfunc_hide() {
super.vfunc_hide();
if (this.menu)
this.menu.close();
}
_onMenuKeyPress(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return Clutter.EVENT_STOP;
let symbol = event.get_key_symbol();
if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right) {
let group = global.focus_manager.get_group(this);
if (group) {
let direction = symbol === Clutter.KEY_Left ? St.DirectionType.LEFT : St.DirectionType.RIGHT;
group.navigate_focus(this, direction, false);
return Clutter.EVENT_STOP;
}
}
return Clutter.EVENT_PROPAGATE;
}
_onOpenStateChanged(menu, open) {
if (open)
this.add_style_pseudo_class('active');
else
this.remove_style_pseudo_class('active');
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let verticalMargins = this.menu.actor.margin_top + this.menu.actor.margin_bottom;
// The workarea and margin dimensions are in physical pixels, but CSS
// measures are in logical pixels, so make sure to consider the scale
// factor when computing max-height
let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor);
this.menu.actor.style = `max-height: ${maxHeight}px;`;
}
_onDestroy() {
if (this.menu)
this.menu.destroy();
super._onDestroy();
}
});
/* SystemIndicator:
*
* This class manages one system indicator, which are the icons
* that you see at the top right. A system indicator is composed
* of an icon and a menu section, which will be composed into the
* aggregate menu.
*/
export const SystemIndicator = GObject.registerClass(
class SystemIndicator extends St.BoxLayout {
_init() {
super._init({
style_class: 'panel-status-indicators-box',
reactive: true,
visible: false,
});
this.menu = new PopupMenu.PopupMenuSection();
}
get indicators() {
let klass = this.constructor.name;
let {stack} = new Error();
log(`Usage of indicator.indicators is deprecated for ${klass}\n${stack}`);
return this;
}
_syncIndicatorsVisible() {
this.visible = this.get_children().some(a => a.visible);
}
_addIndicator() {
let icon = new St.Icon({style_class: 'system-status-icon'});
this.add_actor(icon);
icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this));
this._syncIndicatorsVisible();
return icon;
}
});