89
const MENU_UNSELECTED = 0;
90
const MENU_SELECTED = 1;
91
const MENU_ENTERED = 2;
93
function MenuItem(name, id) {
99
* Shows the list of menus in the sidebar.
101
MenuItem.prototype = {
102
_init: function(name, id) {
105
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
111
this.actor.connect('button-press-event', Lang.bind(this, function (a, e) {
112
this.setState(MENU_SELECTED);
115
this._text = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
116
font_name: "Sans 14px",
119
// We use individual boxes for the label and the arrow to ensure that they
120
// are aligned vertically. Just setting y_align: Big.BoxAlignment.CENTER
121
// on this.actor does not seem to achieve that.
122
let labelBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER,
125
labelBox.append(this._text, Big.BoxPackFlags.NONE);
127
this.actor.append(labelBox, Big.BoxPackFlags.EXPAND);
129
let arrowBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
131
this._arrow = new Shell.Arrow({ surface_width: MENU_ARROW_SIZE,
132
surface_height: MENU_ARROW_SIZE,
133
direction: Gtk.ArrowType.RIGHT,
135
arrowBox.append(this._arrow, Big.BoxPackFlags.NONE);
136
this.actor.append(arrowBox, Big.BoxPackFlags.NONE);
139
getState: function() {
143
setState: function (state) {
144
if (state == this._state)
147
if (this._state == MENU_UNSELECTED) {
148
this.actor.background_color = null;
149
this._arrow.set_opacity(0);
150
} else if (this._state == MENU_ENTERED) {
151
this.actor.background_color = ENTERED_MENU_COLOR;
152
this._arrow.set_opacity(0xFF/2);
154
this.actor.background_color = GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
155
this._arrow.set_opacity(0xFF);
157
this.emit('state-changed')
160
Signals.addSignalMethods(MenuItem.prototype);
162
80
/* This class represents a display containing a collection of application items.
163
81
* The applications are sorted based on their popularity by default, and based on
164
82
* their name if some search filter is applied.
303
221
Signals.addSignalMethods(AppDisplay.prototype);
305
function BaseWellItem(app, isFavorite, hasMenu) {
306
this._init(app, isFavorite, hasMenu);
224
function BaseWellItem(app, isFavorite) {
225
this._init(app, isFavorite);
309
228
BaseWellItem.prototype = {
310
__proto__: AppIcon.AppIcon.prototype,
312
_init: function(app, isFavorite) {
313
AppIcon.AppIcon.prototype._init.call(this, { app: app,
314
menuType: AppIcon.MenuType.ON_RIGHT,
317
this.isFavorite = isFavorite;
229
_init : function(app, isFavorite) {
232
this._glowExtendVertical = 0;
233
this._glowShrinkHorizontal = 0;
235
this.actor = new St.Clickable({ style_class: 'app-well-app',
237
this.actor._delegate = this;
238
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
239
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
241
let box = new St.BoxLayout({ vertical: true });
242
this.actor.set_child(box);
244
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
248
this.icon = this.app.create_icon_texture(APPICON_SIZE);
250
box.add(this.icon, { expand: true, x_fill: false, y_fill: false });
252
let nameBox = new Shell.GenericContainer();
253
nameBox.connect('get-preferred-width', Lang.bind(this, this._nameBoxGetPreferredWidth));
254
nameBox.connect('get-preferred-height', Lang.bind(this, this._nameBoxGetPreferredHeight));
255
nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
256
this._nameBox = nameBox;
258
this._name = new St.Label({ text: this.app.get_name() });
259
this._name.clutter_text.line_alignment = Pango.Alignment.CENTER;
260
nameBox.add_actor(this._name);
261
this._glowBox = new St.BoxLayout({ style_class: 'app-well-app-glow' });
262
this._glowBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
263
this._nameBox.add_actor(this._glowBox);
264
this._glowBox.lower(this._name);
265
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
266
this._rerenderGlow();
319
270
this._draggable = DND.makeDraggable(this.actor, true);
321
// Do these as anonymous functions to avoid conflict with handlers in subclasses
322
this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
323
let [stageX, stageY] = event.get_coords();
324
this._dragStartX = stageX;
325
this._dragStartY = stageY;
328
this.actor.connect('notify::hover', Lang.bind(this, function () {
329
let hover = this.actor.hover;
331
if (this.actor.pressed && this._dragStartX != null) {
332
this.actor.fake_release();
333
this._draggable.startDrag(this._dragStartX, this._dragStartY,
271
this._dragStartX = null;
272
this._dragStartY = null;
274
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
275
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChange));
278
_nameBoxGetPreferredWidth: function (nameBox, forHeight, alloc) {
279
let [min, natural] = this._name.get_preferred_width(forHeight);
280
alloc.min_size = min;
281
alloc.natural_size = natural;
284
_nameBoxGetPreferredHeight: function (nameBox, forWidth, alloc) {
285
let [min, natural] = this._name.get_preferred_height(forWidth);
286
alloc.min_size = min + this._glowExtendVertical * 2;
287
alloc.natural_size = natural + this._glowExtendVertical * 2;
290
_nameBoxAllocate: function (nameBox, box, flags) {
291
let childBox = new Clutter.ActorBox();
292
let [minWidth, naturalWidth] = this._name.get_preferred_width(-1);
293
let [minHeight, naturalHeight] = this._name.get_preferred_height(-1);
294
let availWidth = box.x2 - box.x1;
295
let availHeight = box.y2 - box.y1;
296
let targetWidth = availWidth;
298
if (naturalWidth < availWidth) {
299
xPadding = Math.floor((availWidth - naturalWidth) / 2);
301
childBox.x1 = xPadding;
302
childBox.x2 = availWidth - xPadding;
303
childBox.y1 = this._glowExtendVertical;
304
childBox.y2 = availHeight - this._glowExtendVertical;
305
this._name.allocate(childBox, flags);
308
let glowPaddingHoriz = Math.max(0, xPadding - this._glowShrinkHorizontal);
309
glowPaddingHoriz = Math.max(this._glowShrinkHorizontal, glowPaddingHoriz);
310
childBox.x1 = glowPaddingHoriz;
311
childBox.x2 = availWidth - glowPaddingHoriz;
313
childBox.y2 = availHeight;
314
this._glowBox.allocate(childBox, flags);
317
_onDestroy: function() {
318
if (this._appWindowChangedId > 0)
319
this.app.disconnect(this._appWindowChangedId);
322
_onMapped: function() {
323
if (!this._queuedGlowRerender)
325
this._queuedGlowRerender = false;
326
this._rerenderGlow();
329
_rerenderGlow: function() {
330
if (!this.actor.mapped) {
331
this._queuedGlowRerender = true;
334
this._glowBox.destroy_children();
335
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
336
let windows = this.app.get_windows();
337
for (let i = 0; i < windows.length && i < 3; i++) {
338
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
340
glow.keep_aspect_ratio = false;
341
this._glowBox.add(glow);
345
_onButtonPress: function(actor, event) {
346
let [stageX, stageY] = event.get_coords();
347
this._dragStartX = stageX;
348
this._dragStartY = stageY;
351
_onHoverChange: function(actor) {
352
let hover = this.actor.hover;
354
if (this.actor.pressed && this._dragStartX != null) {
355
this.actor.fake_release();
356
this._draggable.startDrag(this._dragStartX, this._dragStartY,
359
this._dragStartX = null;
360
this._dragStartY = null;
365
_onClicked: function(actor, event) {
366
let button = event.get_button();
368
this._onActivate(event);
369
} else if (button == 3) {
370
// Don't bind to the right click here; we want left click outside the
371
// area to deactivate as well.
377
_onStyleChanged: function() {
378
let themeNode = this._glowBox.get_theme_node();
381
[success, len] = themeNode.get_length('-shell-glow-extend-vertical', false);
383
this._glowExtendVertical = len;
384
[success, len] = themeNode.get_length('-shell-glow-shrink-horizontal', false);
386
this._glowShrinkHorizontal = len;
387
this.actor.queue_relayout();
390
popupMenu: function(activatingButton) {
392
this._menu = new AppIconMenu(this);
393
this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
394
this.highlightWindow(window);
396
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
397
this.activateWindow(window);
399
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
401
this._onMenuPoppedUp();
336
this._dragStartX = null;
337
this._dragStartY = null;
403
this._onMenuPoppedDown();
408
this._menu.popup(activatingButton);
413
// Default implementations; AppDisplay.RunningWellItem overrides these
414
highlightWindow: function(window) {
415
this.emit('highlight-window', window);
418
activateWindow: function(window) {
419
this.emit('activate-window', window);
343
422
shellWorkspaceLaunch : function() {
362
441
return this.actor;
444
Signals.addSignalMethods(BaseWellItem.prototype);
446
function AppIconMenu(source) {
450
AppIconMenu.prototype = {
451
_init: function(source) {
452
this._source = source;
454
this._arrowSize = 4; // CSS default
455
this._spacing = 0; // CSS default
457
this._dragStartX = 0;
458
this._dragStartY = 0;
460
this.actor = new Shell.GenericContainer({ reactive: true });
461
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
462
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
463
this.actor.connect('allocate', Lang.bind(this, this._allocate));
465
this._windowContainerBox = new St.Bin({ style_class: 'app-well-menu' });
466
this._windowContainer = new Shell.Menu({ orientation: Big.BoxOrientation.VERTICAL,
467
width: Main.overview._dash.actor.width });
468
this._windowContainerBox.set_child(this._windowContainer);
469
this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected));
470
this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected));
471
this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled));
472
this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate));
473
this.actor.add_actor(this._windowContainerBox);
475
// Stay popped up on release over application icon
476
this._windowContainer.set_persistent_source(this._source.actor);
478
// Intercept events while the menu has the pointer grab to do window-related effects
479
this._windowContainer.connect('enter-event', Lang.bind(this, this._onMenuEnter));
480
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
481
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
483
this._borderColor = new Clutter.Color();
484
this._backgroundColor = new Clutter.Color();
485
this._windowContainerBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
487
this._arrow = new St.DrawingArea();
488
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
489
Shell.draw_box_pointer(texture,
490
Shell.PointerDirection.LEFT,
492
this._backgroundColor);
494
this.actor.add_actor(this._arrow);
496
// Chain our visibility and lifecycle to that of the source
497
source.actor.connect('notify::mapped', Lang.bind(this, function () {
498
if (!source.actor.mapped)
499
this._windowContainer.popdown();
501
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
503
global.stage.add_actor(this.actor);
506
_getPreferredWidth: function(actor, forHeight, alloc) {
507
let [min, natural] = this._windowContainerBox.get_preferred_width(forHeight);
508
min += this._arrowSize;
509
natural += this._arrowSize;
510
alloc.min_size = min;
511
alloc.natural_size = natural;
514
_getPreferredHeight: function(actor, forWidth, alloc) {
515
let [min, natural] = this._windowContainerBox.get_preferred_height(forWidth);
516
alloc.min_size = min;
517
alloc.natural_size = natural;
520
_allocate: function(actor, box, flags) {
521
let childBox = new Clutter.ActorBox();
522
let themeNode = this._windowContainerBox.get_theme_node();
524
let width = box.x2 - box.x1;
525
let height = box.y2 - box.y1;
528
childBox.x2 = this._arrowSize;
529
childBox.y1 = Math.floor((height / 2) - (this._arrowSize / 2));
530
childBox.y2 = childBox.y1 + this._arrowSize;
531
this._arrow.allocate(childBox, flags);
533
// Ensure the arrow is above the border area
534
let border = themeNode.get_border_width(St.Side.LEFT);
535
childBox.x1 = this._arrowSize - border;
538
childBox.y2 = height;
539
this._windowContainerBox.allocate(childBox, flags);
542
_redisplay: function() {
543
this._windowContainer.remove_all();
545
let windows = this._source.app.get_windows();
547
this._windowContainer.show();
549
let iconsDiffer = false;
550
let texCache = Shell.TextureCache.get_default();
551
if (windows.length > 0) {
552
let firstIcon = windows[0].mini_icon;
553
for (let i = 1; i < windows.length; i++) {
554
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
561
// Display the app windows menu items and the separator between windows
562
// of the current desktop and other windows.
563
let activeWorkspace = global.screen.get_active_workspace();
564
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
566
for (let i = 0; i < windows.length; i++) {
567
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
568
this._appendSeparator();
569
separatorShown = true;
571
let box = this._appendMenuItem(windows[i].title);
572
box._window = windows[i];
575
if (windows.length > 0)
576
this._appendSeparator();
578
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
580
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
582
if (windows.length > 0)
583
this._appendSeparator();
584
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
585
: _("Add to Favorites"));
587
this._highlightedItem = null;
590
_appendSeparator: function () {
591
let bin = new St.Bin({ style_class: "app-well-menu-separator" });
592
this._windowContainer.append_separator(bin, Big.BoxPackFlags.NONE);
595
_appendMenuItem: function(labelText) {
596
let box = new St.BoxLayout({ style_class: 'app-well-menu-item',
598
let label = new St.Label({ text: labelText });
600
this._windowContainer.append(box, Big.BoxPackFlags.NONE);
604
popup: function(activatingButton) {
605
let [stageX, stageY] = this._source.actor.get_transformed_position();
606
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
610
this._windowContainer.popup(activatingButton, Main.currentTime());
612
this.emit('popup', true);
615
x = Math.floor(stageX + stageWidth);
616
y = Math.floor(stageY + (stageHeight / 2) - (this.actor.height / 2));
618
this.actor.set_position(x, y);
622
popdown: function() {
623
this._windowContainer.popdown();
624
this.emit('popup', false);
628
selectWindow: function(metaWindow) {
629
this._selectMenuItemForWindow(metaWindow);
632
_findMetaWindowForActor: function (actor) {
633
if (actor._delegate instanceof Workspaces.WindowClone)
634
return actor._delegate.metaWindow;
635
else if (actor.get_meta_window)
636
return actor.get_meta_window();
640
// This function is called while the menu has a pointer grab; what we want
641
// to do is see if the mouse was released over a window representation
642
_onMenuButtonRelease: function (actor, event) {
643
let metaWindow = this._findMetaWindowForActor(event.get_source());
645
this.emit('activate-window', metaWindow);
649
_updateHighlight: function (item) {
650
if (this._highlightedItem) {
651
this._highlightedItem.set_style_pseudo_class(null);
652
this.emit('highlight-window', null);
654
this._highlightedItem = item;
655
if (this._highlightedItem) {
656
item.set_style_pseudo_class('hover');
657
let window = this._highlightedItem._window;
659
this.emit('highlight-window', window);
663
_selectMenuItemForWindow: function (metaWindow) {
664
let children = this._windowContainer.get_children();
665
for (let i = 0; i < children.length; i++) {
666
let child = children[i];
667
let menuMetaWindow = child._window;
668
if (menuMetaWindow == metaWindow)
669
this._updateHighlight(child);
673
// Called while menu has a pointer grab
674
_onMenuEnter: function (actor, event) {
675
let metaWindow = this._findMetaWindowForActor(event.get_source());
677
this._selectMenuItemForWindow(metaWindow);
681
// Called while menu has a pointer grab
682
_onMenuLeave: function (actor, event) {
683
let metaWindow = this._findMetaWindowForActor(event.get_source());
685
this._updateHighlight(null);
689
_onItemUnselected: function (actor, child) {
690
this._updateHighlight(null);
693
_onItemSelected: function (actor, child) {
694
this._updateHighlight(child);
697
_onItemActivate: function (actor, child) {
699
let metaWindow = child._window;
700
this.emit('activate-window', metaWindow);
701
} else if (child == this._newWindowMenuItem) {
702
this._source.app.launch();
703
this.emit('activate-window', null);
704
} else if (child == this._toggleFavoriteMenuItem) {
705
let favs = AppFavorites.getAppFavorites();
706
let isFavorite = favs.isFavorite(this._source.app.get_id());
708
favs.removeFavorite(this._source.app.get_id());
710
favs.addFavorite(this._source.app.get_id());
715
_onWindowSelectionCancelled: function () {
716
this.emit('highlight-window', null);
720
_onStyleChanged: function() {
721
let themeNode = this._windowContainerBox.get_theme_node();
722
let [success, len] = themeNode.get_length('-shell-arrow-size', false);
724
this._arrowSize = len;
725
this.actor.queue_relayout();
727
[success, len] = themeNode.get_length('-shell-menu-spacing', false)
729
this._windowContainer.spacing = len;
731
let color = new Clutter.Color();
732
if (themeNode.get_background_color(color)) {
733
this._backgroundColor = color;
734
color = new Clutter.Color();
736
if (themeNode.get_border_color(St.Side.LEFT, color)) {
737
this._borderColor = color;
739
this._arrow.emit_redraw();
742
Signals.addSignalMethods(AppIconMenu.prototype);
366
744
function RunningWellItem(app, isFavorite) {
367
745
this._init(app, isFavorite);
456
822
WellGrid.prototype = {
457
823
_init: function() {
458
this.actor = new Shell.GenericContainer();
460
this._separator = new Big.Box({ height: 1 });
461
this.actor.add_actor(this._separator);
462
this._separatorIndex = 0;
463
this._cachedSeparatorY = 0;
465
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
466
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
467
this.actor.connect('allocate', Lang.bind(this, this._allocate));
824
this.actor = new St.Bin({ name: "dashAppWell" });
825
// Pulled from CSS, but hardcode some defaults here
827
this._item_size = 48;
828
this._grid = new Shell.GenericContainer();
829
this.actor.set_child(this._grid);
830
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
832
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
833
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
834
this._grid.connect('allocate', Lang.bind(this, this._allocate));
470
837
_getPreferredWidth: function (grid, forHeight, alloc) {
471
let [itemMin, itemNatural] = this._getItemPreferredWidth();
472
let children = this._getItemChildren();
474
if (children.length < WELL_DEFAULT_COLUMNS)
475
nColumns = children.length;
838
let children = this._grid.get_children();
839
let nColumns = children.length;
840
let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
841
// Kind of a lie, but not really an issue right now. If
842
// we wanted to support some sort of hidden/overflow that would
843
// need higher level design
844
alloc.min_size = this._item_size;
845
alloc.natural_size = nColumns * this._item_size + totalSpacing;
848
_getPreferredHeight: function (grid, forWidth, alloc) {
849
let children = this._grid.get_children();
850
let [nColumns, usedWidth] = this._computeLayout(forWidth);
853
nRows = Math.ceil(children.length / nColumns);
477
nColumns = WELL_DEFAULT_COLUMNS;
478
alloc.min_size = itemMin;
479
alloc.natural_size = itemNatural * nColumns;
482
_getPreferredHeight: function (grid, forWidth, alloc) {
483
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
484
let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
486
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(forWidth);
487
alloc.min_size = alloc.natural_size = rows * itemHeight + totalVerticalSpacing + separatorNatural;
856
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
857
let height = nRows * this._item_size + totalSpacing;
858
alloc.min_size = height;
859
alloc.natural_size = height;
490
862
_allocate: function (grid, box, flags) {
491
let children = this._getItemChildren();
863
let children = this._grid.get_children();
492
864
let availWidth = box.x2 - box.x1;
493
865
let availHeight = box.y2 - box.y1;
495
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(availWidth);
497
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(-1);
867
let [nColumns, usedWidth] = this._computeLayout(availWidth);
869
let overallPaddingX = Math.floor((availWidth - usedWidth) / 2);
871
let x = box.x1 + overallPaddingX;
501
873
let columnIndex = 0;
502
874
for (let i = 0; i < children.length; i++) {
503
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
875
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
876
= children[i].get_preferred_size();
505
878
/* Center the item in its allocation horizontally */
506
let width = Math.min(itemWidth, childNaturalWidth);
507
let horizSpacing = (itemWidth - width) / 2;
879
let width = Math.min(this._item_size, childNaturalWidth);
880
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
881
let height = Math.min(this._item_size, childNaturalHeight);
882
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
509
884
let childBox = new Clutter.ActorBox();
510
childBox.x1 = Math.floor(x + horizSpacing);
885
childBox.x1 = Math.floor(x + childXSpacing);
886
childBox.y1 = Math.floor(y + childYSpacing);
512
887
childBox.x2 = childBox.x1 + width;
513
childBox.y2 = childBox.y1 + itemHeight;
888
childBox.y2 = childBox.y1 + height;
514
889
children[i].allocate(childBox, flags);
517
if (columnIndex == columns) {
892
if (columnIndex == nColumns) {
521
896
if (columnIndex == 0) {
522
y += itemHeight + WELL_ITEM_VSPACING;
897
y += this._item_size + this._spacing;
898
x = box.x1 + overallPaddingX;
900
x += this._item_size + this._spacing;
530
removeAll: function () {
531
let itemChildren = this._getItemChildren();
532
for (let i = 0; i < itemChildren.length; i++) {
533
itemChildren[i].destroy();
537
_getItemChildren: function () {
538
let children = this.actor.get_children();
543
905
_computeLayout: function (forWidth) {
544
let [itemMinWidth, itemNaturalWidth] = this._getItemPreferredWidth();
547
let children = this._getItemChildren();
548
if (children.length == 0)
549
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
906
let children = this._grid.get_children();
550
907
let nColumns = 0;
551
908
let usedWidth = 0;
552
// Big.Box will allocate us at 0x0 if we are not visible; this is probably a
553
// Big.Box bug but it can't be fixed because if children are skipped in allocate()
554
// Clutter gets confused (see http://bugzilla.openedhand.com/show_bug.cgi?id=1831)
556
nColumns = WELL_DEFAULT_COLUMNS;
558
while (nColumns < WELL_DEFAULT_COLUMNS &&
559
nColumns < children.length &&
560
usedWidth + itemMinWidth <= forWidth) {
561
// By including WELL_ITEM_MIN_HSPACING in usedWidth, we are ensuring
562
// that the number of columns we end up with will allow the spacing
563
// between the columns to be at least that value.
564
usedWidth += itemMinWidth + WELL_ITEM_MIN_HSPACING;
570
log("WellGrid: couldn't fit a column in width " + forWidth);
571
/* FIXME - fall back to smaller icon size */
574
let minWidth = itemMinWidth * nColumns;
576
let lastColumnIndex = nColumns - 1;
577
let rows = Math.ceil(children.length / nColumns);
581
itemWidth = itemNaturalWidth;
583
itemWidth = Math.floor(forWidth / nColumns);
586
let itemNaturalHeight = 0;
587
for (let i = 0; i < children.length; i++) {
588
let [childMin, childNatural] = children[i].get_preferred_height(itemWidth);
589
if (childNatural > itemNaturalHeight)
590
itemNaturalHeight = childNatural;
593
return [rows, nColumns, itemWidth, itemNaturalHeight];
596
_getItemPreferredWidth: function () {
597
let children = this._getItemChildren();
599
let naturalWidth = 0;
600
for (let i = 0; i < children.length; i++) {
601
let [childMin, childNatural] = children[i].get_preferred_width(-1);
602
if (childMin > minWidth)
604
if (childNatural > naturalWidth)
605
naturalWidth = childNatural;
607
return [minWidth, naturalWidth];
909
while (nColumns < WELL_MAX_COLUMNS &&
910
nColumns < children.length &&
911
(usedWidth + this._item_size <= forWidth)) {
912
usedWidth += this._item_size + this._spacing;
917
usedWidth -= this._spacing;
919
return [nColumns, usedWidth];
922
_onStyleChanged: function() {
923
let themeNode = this.actor.get_theme_node();
924
let [success, len] = themeNode.get_length('spacing', false);
927
[success, len] = themeNode.get_length('-shell-grid-item-size', false);
929
this._item_size = len;
930
this._grid.queue_relayout();
933
removeAll: function () {
934
this._grid.get_children().forEach(Lang.bind(this, function (child) {
939
addItem: function(actor) {
940
this._grid.add_actor(actor);