// -*- 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 St = imports.gi.St; const BoxPointer = imports.ui.boxpointer; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const MAX_CANDIDATES_PER_PAGE = 16; const CandidateArea = new Lang.Class({ Name: 'CandidateArea', Extends: PopupMenu.PopupBaseMenuItem, _init: function() { this.parent({ reactive: false }); // St.Table exhibits some sizing problems so let's go with a // clutter layout manager for now. this._table = new Clutter.Actor(); this.addActor(this._table); this._tableLayout = new Clutter.TableLayout(); this._table.set_layout_manager(this._tableLayout); this._indexLabels = []; this._candidateLabels = []; for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { this._indexLabels.push(new St.Label({ style_class: 'candidate-index' })); this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' })); } this._orientation = -1; this._cursorPosition = 0; }, _setOrientation: function(orientation) { if (this._orientation == orientation) return; this._orientation = orientation; this._table.remove_all_children(); if (this._orientation == IBus.Orientation.HORIZONTAL) for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { this._tableLayout.pack(this._indexLabels[i], i*2, 0); this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0); } else // VERTICAL || SYSTEM for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { this._tableLayout.pack(this._indexLabels[i], 0, i); this._tableLayout.pack(this._candidateLabels[i], 1, i); } }, setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) { this._setOrientation(orientation); for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { let visible = i < candidates.length; this._indexLabels[i].visible = visible; this._candidateLabels[i].visible = visible; if (!visible) continue; this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1)); this._candidateLabels[i].text = candidates[i]; } this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected'); this._cursorPosition = cursorPosition; if (cursorVisible) this._candidateLabels[cursorPosition].add_style_pseudo_class('selected'); }, }); const CandidatePopup = new Lang.Class({ Name: 'CandidatePopup', Extends: PopupMenu.PopupMenu, _init: function() { this._cursor = new St.Bin({ opacity: 0 }); Main.uiGroup.add_actor(this._cursor); this.parent(this._cursor, 0, St.Side.TOP); this.actor.hide(); Main.uiGroup.add_actor(this.actor); this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false }); this._preeditTextItem.actor.hide(); this.addMenuItem(this._preeditTextItem); this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false }); this._auxTextItem.actor.hide(); this.addMenuItem(this._auxTextItem); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._lookupTableItem = new CandidateArea(); this._lookupTableItem.actor.hide(); this.addMenuItem(this._lookupTableItem); 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._cursor.set_position(x, y); this._cursor.set_size(w, h); })); panelService.connect('update-preedit-text', Lang.bind(this, function(ps, text, cursorPosition, visible) { if (visible) this._preeditTextItem.actor.show(); else this._preeditTextItem.actor.hide(); this._updateVisibility(); this._preeditTextItem.actor.label_actor.text = text.get_text(); let attrs = text.get_attributes(); if (attrs) this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text, attrs); })); panelService.connect('show-preedit-text', Lang.bind(this, function(ps) { this._preeditTextItem.actor.show(); this._updateVisibility(); })); panelService.connect('hide-preedit-text', Lang.bind(this, function(ps) { this._preeditTextItem.actor.hide(); this._updateVisibility(); })); panelService.connect('update-auxiliary-text', Lang.bind(this, function(ps, text, visible) { if (visible) this._auxTextItem.actor.show(); else this._auxTextItem.actor.hide(); this._updateVisibility(); this._auxTextItem.actor.label_actor.text = text.get_text(); })); panelService.connect('show-auxiliary-text', Lang.bind(this, function(ps) { this._auxTextItem.actor.show(); this._updateVisibility(); })); panelService.connect('hide-auxiliary-text', Lang.bind(this, function(ps) { this._auxTextItem.actor.hide(); this._updateVisibility(); })); panelService.connect('update-lookup-table', Lang.bind(this, function(ps, lookupTable, visible) { if (visible) this._lookupTableItem.actor.show(); else this._lookupTableItem.actor.hide(); this._updateVisibility(); let cursorPos = lookupTable.get_cursor_pos(); let pageSize = lookupTable.get_page_size(); let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize)); let startIndex = page * pageSize; let endIndex = Math.min((page + 1) * pageSize, lookupTable.get_number_of_candidates()); 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._lookupTableItem.setCandidates(indexes, candidates, lookupTable.get_orientation(), cursorPos % pageSize, lookupTable.is_cursor_visible()); })); panelService.connect('show-lookup-table', Lang.bind(this, function(ps) { this._lookupTableItem.actor.show(); this._updateVisibility(); })); panelService.connect('hide-lookup-table', Lang.bind(this, function(ps) { this._lookupTableItem.actor.hide(); this._updateVisibility(); })); panelService.connect('focus-out', Lang.bind(this, function(ps) { this.close(BoxPointer.PopupAnimation.NONE); })); }, _updateVisibility: function() { let isVisible = (this._preeditTextItem.actor.visible || this._auxTextItem.actor.visible || this._lookupTableItem.actor.visible); if (isVisible) this.open(BoxPointer.PopupAnimation.NONE); else this.close(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()); } });