File: ibusCandidatePopup.js

package info (click to toggle)
gnome-shell 3.14.4-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 14,412 kB
  • ctags: 7,297
  • sloc: ansic: 45,021; sh: 11,312; xml: 875; makefile: 696; cpp: 57; python: 39; perl: 13
file content (269 lines) | stat: -rw-r--r-- 12,267 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// -*- 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' });
        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' });
        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) {
                                 Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
                                 if (this._boxPointer.actor.visible)
                                     this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
                             }));
        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);
                             }));
    },

    _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());
    }
});