.settings
browser-plugin
data
docs
js
extensionPrefs
gdm
misc
perf
portalHelper
ui
components
status
altTab.js
animation.js
appDisplay.js
appFavorites.js
audioDeviceSelection.js
background.js
backgroundMenu.js
boxpointer.js
calendar.js
checkBox.js
ctrlAltTab.js
dash.js
dateMenu.js
dnd.js
edgeDragAction.js
endSessionDialog.js
environment.js
extensionDownloader.js
extensionSystem.js
focusCaretTracker.js
grabHelper.js
ibusCandidatePopup.js
iconGrid.js
keyboard.js
layout.js
legacyTray.js
lightbox.js
lookingGlass.js
magnifier.js
magnifierDBus.js
main.js
messageList.js
messageTray.js
modalDialog.js
mpris.js
notificationDaemon.js
osdMonitorLabeler.js
osdWindow.js
overview.js
overviewControls.js
panel.js
panelMenu.js
pointerWatcher.js
popupMenu.js
remoteMenu.js
remoteSearch.js
runDialog.js
screenShield.js
screencast.js
screenshot.js
scripting.js
search.js
separator.js
sessionMode.js
shellDBus.js
shellEntry.js
shellMountOperation.js
slider.js
switcherPopup.js
tweener.js
unlockDialog.js
userWidget.js
viewSelector.js
windowAttentionHandler.js
windowManager.js
windowMenu.js
workspace.js
workspaceSwitcherPopup.js
workspaceThumbnail.js
workspacesView.js
xdndHandler.js
Makefile.am
js-resources.gresource.xml
man
po
src
tests
tools
.gitignore
.gitmodules
.project
AUTHORS
COPYING
HACKING
MAINTAINERS
Makefile.am
NEWS
README
autogen.sh
configure.ac
gnome-shell.doap

Only recent IBus versions have support for this signal which is used for wayland clients. In order to work with older IBus versions we can silently ignore the signal's absence. https://bugzilla.gnome.org/show_bug.cgi?id=753476
288 lines
13 KiB
JavaScript
288 lines
13 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const IBus = imports.gi.IBus;
|
|
const Lang = imports.lang;
|
|
const Signals = imports.signals;
|
|
const St = imports.gi.St;
|
|
|
|
const BoxPointer = imports.ui.boxpointer;
|
|
const Main = imports.ui.main;
|
|
|
|
const MAX_CANDIDATES_PER_PAGE = 16;
|
|
|
|
const DEFAULT_INDEX_LABELS = [ '1', '2', '3', '4', '5', '6', '7', '8',
|
|
'9', '0', 'a', 'b', 'c', 'd', 'e', 'f' ];
|
|
|
|
const CandidateArea = new Lang.Class({
|
|
Name: 'CandidateArea',
|
|
|
|
_init: function() {
|
|
this.actor = new St.BoxLayout({ vertical: true,
|
|
visible: false });
|
|
this._candidateBoxes = [];
|
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
|
let box = new St.BoxLayout({ style_class: 'candidate-box',
|
|
reactive: true,
|
|
track_hover: true });
|
|
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
|
|
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
|
|
box.add(box._indexLabel, { y_fill: false });
|
|
box.add(box._candidateLabel, { y_fill: false });
|
|
this._candidateBoxes.push(box);
|
|
this.actor.add(box);
|
|
|
|
let j = i;
|
|
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
|
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}));
|
|
}
|
|
|
|
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
|
|
|
|
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous button' });
|
|
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
|
this._buttonBox.add(this._previousButton, { expand: true });
|
|
|
|
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next button' });
|
|
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
|
this._buttonBox.add(this._nextButton, { expand: true });
|
|
|
|
this.actor.add(this._buttonBox);
|
|
|
|
this._previousButton.connect('clicked', Lang.bind(this, function() {
|
|
this.emit('previous-page');
|
|
}));
|
|
this._nextButton.connect('clicked', Lang.bind(this, function() {
|
|
this.emit('next-page');
|
|
}));
|
|
|
|
this._orientation = -1;
|
|
this._cursorPosition = 0;
|
|
},
|
|
|
|
setOrientation: function(orientation) {
|
|
if (this._orientation == orientation)
|
|
return;
|
|
|
|
this._orientation = orientation;
|
|
|
|
if (this._orientation == IBus.Orientation.HORIZONTAL) {
|
|
this.actor.vertical = false;
|
|
this.actor.remove_style_class_name('vertical');
|
|
this.actor.add_style_class_name('horizontal');
|
|
this._previousButton.child.icon_name = 'go-previous-symbolic';
|
|
this._nextButton.child.icon_name = 'go-next-symbolic';
|
|
} else { // VERTICAL || SYSTEM
|
|
this.actor.vertical = true;
|
|
this.actor.add_style_class_name('vertical');
|
|
this.actor.remove_style_class_name('horizontal');
|
|
this._previousButton.child.icon_name = 'go-up-symbolic';
|
|
this._nextButton.child.icon_name = 'go-down-symbolic';
|
|
}
|
|
},
|
|
|
|
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
|
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
|
let visible = i < candidates.length;
|
|
let box = this._candidateBoxes[i];
|
|
box.visible = visible;
|
|
|
|
if (!visible)
|
|
continue;
|
|
|
|
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : DEFAULT_INDEX_LABELS[i]);
|
|
box._candidateLabel.text = candidates[i];
|
|
}
|
|
|
|
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
|
|
this._cursorPosition = cursorPosition;
|
|
if (cursorVisible)
|
|
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
|
|
},
|
|
|
|
updateButtons: function(wrapsAround, page, nPages) {
|
|
if (nPages < 2) {
|
|
this._buttonBox.hide();
|
|
return;
|
|
}
|
|
this._buttonBox.show();
|
|
this._previousButton.reactive = wrapsAround || page > 0;
|
|
this._nextButton.reactive = wrapsAround || page < nPages - 1;
|
|
},
|
|
});
|
|
Signals.addSignalMethods(CandidateArea.prototype);
|
|
|
|
const CandidatePopup = new Lang.Class({
|
|
Name: 'CandidatePopup',
|
|
|
|
_init: function() {
|
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
|
|
this._boxPointer.actor.visible = false;
|
|
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
|
|
Main.layoutManager.addChrome(this._boxPointer.actor);
|
|
|
|
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
|
|
vertical: true });
|
|
this._boxPointer.bin.set_child(box);
|
|
|
|
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
|
|
visible: false });
|
|
box.add(this._preeditText);
|
|
|
|
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
|
|
visible: false });
|
|
box.add(this._auxText);
|
|
|
|
this._candidateArea = new CandidateArea();
|
|
box.add(this._candidateArea.actor);
|
|
|
|
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
|
|
this._panelService.page_up();
|
|
}));
|
|
this._candidateArea.connect('next-page', Lang.bind(this, function() {
|
|
this._panelService.page_down();
|
|
}));
|
|
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
|
|
this._panelService.candidate_clicked(index, button, state);
|
|
}));
|
|
|
|
this._panelService = null;
|
|
},
|
|
|
|
setPanelService: function(panelService) {
|
|
this._panelService = panelService;
|
|
if (!panelService)
|
|
return;
|
|
|
|
panelService.connect('set-cursor-location',
|
|
Lang.bind(this, function(ps, x, y, w, h) {
|
|
this._setDummyCursorGeometry(x, y, w, h);
|
|
}));
|
|
try {
|
|
panelService.connect('set-cursor-location-relative',
|
|
Lang.bind(this, function(ps, x, y, w, h) {
|
|
if (!global.display.focus_window)
|
|
return;
|
|
let window = global.display.focus_window.get_compositor_private();
|
|
this._setDummyCursorGeometry(window.x + x, window.y + y, w, h);
|
|
}));
|
|
} catch(e) {
|
|
// Only recent IBus versions have support for this signal
|
|
// which is used for wayland clients. In order to work
|
|
// with older IBus versions we can silently ignore the
|
|
// signal's absence.
|
|
}
|
|
panelService.connect('update-preedit-text',
|
|
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
|
this._preeditText.visible = visible;
|
|
this._updateVisibility();
|
|
|
|
this._preeditText.text = text.get_text();
|
|
|
|
let attrs = text.get_attributes();
|
|
if (attrs)
|
|
this._setTextAttributes(this._preeditText.clutter_text,
|
|
attrs);
|
|
}));
|
|
panelService.connect('show-preedit-text',
|
|
Lang.bind(this, function(ps) {
|
|
this._preeditText.show();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('hide-preedit-text',
|
|
Lang.bind(this, function(ps) {
|
|
this._preeditText.hide();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('update-auxiliary-text',
|
|
Lang.bind(this, function(ps, text, visible) {
|
|
this._auxText.visible = visible;
|
|
this._updateVisibility();
|
|
|
|
this._auxText.text = text.get_text();
|
|
}));
|
|
panelService.connect('show-auxiliary-text',
|
|
Lang.bind(this, function(ps) {
|
|
this._auxText.show();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('hide-auxiliary-text',
|
|
Lang.bind(this, function(ps) {
|
|
this._auxText.hide();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('update-lookup-table',
|
|
Lang.bind(this, function(ps, lookupTable, visible) {
|
|
this._candidateArea.actor.visible = visible;
|
|
this._updateVisibility();
|
|
|
|
let nCandidates = lookupTable.get_number_of_candidates();
|
|
let cursorPos = lookupTable.get_cursor_pos();
|
|
let pageSize = lookupTable.get_page_size();
|
|
let nPages = Math.ceil(nCandidates / pageSize);
|
|
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
|
let startIndex = page * pageSize;
|
|
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
|
|
|
|
let indexes = [];
|
|
let indexLabel;
|
|
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
|
indexes.push(indexLabel.get_text());
|
|
|
|
let candidates = [];
|
|
for (let i = startIndex; i < endIndex; ++i)
|
|
candidates.push(lookupTable.get_candidate(i).get_text());
|
|
|
|
this._candidateArea.setCandidates(indexes,
|
|
candidates,
|
|
cursorPos % pageSize,
|
|
lookupTable.is_cursor_visible());
|
|
this._candidateArea.setOrientation(lookupTable.get_orientation());
|
|
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
|
|
}));
|
|
panelService.connect('show-lookup-table',
|
|
Lang.bind(this, function(ps) {
|
|
this._candidateArea.actor.show();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('hide-lookup-table',
|
|
Lang.bind(this, function(ps) {
|
|
this._candidateArea.actor.hide();
|
|
this._updateVisibility();
|
|
}));
|
|
panelService.connect('focus-out',
|
|
Lang.bind(this, function(ps) {
|
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
|
}));
|
|
},
|
|
|
|
_setDummyCursorGeometry: function(x, y, w, h) {
|
|
Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
|
|
if (this._boxPointer.actor.visible)
|
|
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
|
},
|
|
|
|
_updateVisibility: function() {
|
|
let isVisible = (this._preeditText.visible ||
|
|
this._auxText.visible ||
|
|
this._candidateArea.actor.visible);
|
|
|
|
if (isVisible) {
|
|
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
|
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
|
|
this._boxPointer.actor.raise_top();
|
|
} else {
|
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
|
}
|
|
},
|
|
|
|
_setTextAttributes: function(clutterText, ibusAttrList) {
|
|
let attr;
|
|
for (let i = 0; attr = ibusAttrList.get(i); ++i)
|
|
if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
|
|
clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
|
|
}
|
|
});
|