diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 2f400e256..d24d71916 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -144,6 +144,22 @@ StTooltip { height: 1em; } +/* Switches (to be used in menus) */ +.switch { + border-radius: 5px; + border: 1px solid #ffffff; + padding: 0px 0.2em; +} +.switch-label { + font-size: 0.8em; + padding: 2px 0.1em; /* account for border if checked */ +} +.switch-label:checked { + border: 2px solid #ffffff; + border-radius: 4px; + padding: 0px 0.3em; +} + /* Panel */ #panel { diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index ac569f7e0..0c7bf2995 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -11,8 +11,49 @@ const Main = imports.ui.main; const BoxPointer = imports.ui.boxpointer; const Tweener = imports.ui.tweener; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + const POPUP_ANIMATION_TIME = 0.1; +function Switch() { + this._init.apply(this, arguments); +} + +Switch.prototype = { + _init: function(state) { + this.actor = new St.BoxLayout({ style_class: 'switch' }); + // Translators: the "ON" and "OFF" strings are used in the + // toggle switches in the status area menus, and must be SHORT. + // If you don't have suitable short words, consider initials, + // "0"/"1", "⚪"/"⚫", etc. + this._on = new St.Label({ style_class: 'switch-label', text: _("ON") }); + this._off = new St.Label({ style_class: 'switch-label', text: _("OFF") }); + this.actor.add(this._on, { fill: true }); + this.actor.add(this._off, { fill: true }); + this.setToggleState(state); + }, + + setToogleState: function(state) { + if (state) { + this._on.remove_style_pseudo_class('checked'); + this._on.text = _("ON"); + this._off.add_style_pseudo_class('checked'); + this._off.text = '|||'; + } else { + this._off.remove_style_pseudo_class('checked'); + this._off.text = _("OFF"); + this._on.add_style_pseudo_class('checked'); + this._on.text = '|||'; + } + this.state = state; + }, + + toggle: function() { + this.setToggleState(!this.state); + } +}; + function PopupBaseMenuItem(reactive) { this._init(reactive); } @@ -113,6 +154,45 @@ PopupSeparatorMenuItem.prototype = { } }; +function PopupSwitchMenuItem() { + this._init.apply(this, arguments); +} + +PopupSwitchMenuItem.prototype = { + __proto__: PopupBaseMenuItem.prototype, + + _init: function(text, active) { + PopupBaseMenuItem.prototype._init.call(this, true); + + this.active = !!active; + this.label = new St.Label({ text: text }); + this._switch = new Switch(this.active); + + this._box = new St.BoxLayout({ style_class: 'popup-switch-menu-item' }); + this._box.add(this.label,{ expand: true }); + this._box.add(this._switch.actor); + this.actor.set_child(this._box); + + this.connect('activate', Lang.bind(this,function(from) { + this.toggle(); + })); + }, + + toggle: function() { + this._switch.toggle(); + this.emit('toggled', this._switch.state); + }, + + get state() { + return this._switch.state; + }, + + setToggleState: function(state) { + this._switch.setToggleState(state); + } +} + + function PopupImageMenuItem(text, iconName, alwaysShowImage) { this._init(text, iconName, alwaysShowImage); } diff --git a/po/POTFILES.in b/po/POTFILES.in index 352d6b8bb..bd4f23b6b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,7 @@ js/ui/lookingGlass.js js/ui/overview.js js/ui/panel.js js/ui/placeDisplay.js +js/ui/popupMenu.js js/ui/runDialog.js js/ui/statusMenu.js js/ui/windowAttentionHandler.js