app-display: Implement filtering applications by category

Add a list of filters to the application view of the view selector, as in the latest mockups

https://bugzilla.gnome.org/show_bug.cgi?id=631537
This commit is contained in:
Maxim Ermilov 2010-12-18 22:18:10 +03:00
parent bdfc516715
commit 4fd24da4e4
5 changed files with 231 additions and 83 deletions

View File

@ -27,6 +27,7 @@ dist_theme_DATA = \
theme/corner-ripple.png \ theme/corner-ripple.png \
theme/dash-placeholder.svg \ theme/dash-placeholder.svg \
theme/dialog-error.svg \ theme/dialog-error.svg \
theme/filter-selected.svg \
theme/gnome-shell.css \ theme/gnome-shell.css \
theme/mosaic-view-active.svg \ theme/mosaic-view-active.svg \
theme/mosaic-view.svg \ theme/mosaic-view.svg \

View File

@ -1,12 +1,30 @@
<Menu> <Menu>
<DefaultLayout> <DefaultLayout>
<Menuname>Apps</Menuname> <Menuname>Accessories</Menuname>
<Menuname>Games</Menuname> <Menuname>Games</Menuname>
<Menuname>Tools</Menuname> <Menuname>Graphics</Menuname>
</DefaultLayout> <Menuname>Internet</Menuname>
<Menuname>Multimedia</Menuname>
<Menuname>Office</Menuname>
<Menuname>Other</Menuname>
</DefaultLayout>
<Name>Applications</Name> <Name>Applications</Name>
<AppDir>/usr/local/share/applications</AppDir> <AppDir>/usr/local/share/applications</AppDir>
<DefaultAppDirs/> <DefaultAppDirs/>
<Menu>
<Name>Accessories</Name>
<Include>
<And>
<Category>Utility</Category>
<Not>
<Category>System</Category>
</Not>
</And>
</Include>
</Menu>
<Menu> <Menu>
<Name>Games</Name> <Name>Games</Name>
<Include> <Include>
@ -15,21 +33,47 @@
</And> </And>
</Include> </Include>
</Menu> </Menu>
<Menu> <Menu>
<Name>Tools</Name> <Name>Graphics</Name>
<Include> <Include>
<Category>Development</Category>
<And> <And>
<Category>System</Category> <Category>Graphics</Category>
<Not>
<Category>Settings</Category>
</Not>
</And> </And>
<Category>Utility</Category>
</Include> </Include>
</Menu> </Menu>
<Menu> <Menu>
<Name>Apps</Name> <Name>Internet</Name>
<Include>
<And>
<Category>Network</Category>
<Not><Category>Settings</Category></Not>
</And>
</Include>
</Menu>
<Menu>
<Name>Multimedia</Name>
<Include>
<And>
<Category>AudioVideo</Category>
<Not><Category>Settings</Category></Not>
</And>
</Include>
</Menu>
<Menu>
<Name>Office</Name>
<Include>
<And>
<Category>Office</Category>
</And>
</Include>
</Menu>
<Menu>
<Name>Other</Name>
<OnlyUnallocated/> <OnlyUnallocated/>
<Include> <Include>
<And> <And>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10"
height="20"
id="svg10003"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="filter-selected.svg">
<defs
id="defs10005">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective10011" />
<inkscape:perspective
id="perspective9998"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="10.181818"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1680"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10008">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-44)">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="rect34320"
d="m -0.18726572,54.181804 10.55634072,10.55636 10e-6,-21.11269 z"
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -470,26 +470,28 @@ StTooltip StLabel {
/* Apps */ /* Apps */
.overview-pane {
width: 440px;
}
.icon-grid { .icon-grid {
spacing: 36px; spacing: 36px;
-shell-grid-item-size: 70px; -shell-grid-item-size: 70px;
} }
.all-app { .all-app {
padding: 16px 250px 10px 16px; padding: 16px 25px 16px 16px;
spacing: 20px;
} }
.app-section-divider-container { .app-filter {
padding-top: 36px; font-size: 14px;
padding-bottom: 36px; font-weight: bold;
height: 40px;
color: #aaa;
width: 200px;
} }
.app-section-divider { .app-filter:selected {
height: 2px; color: #ffffff;
background-image: url("filter-selected.svg");
background-position: 190px 10px;
} }
#dash > .app-well-app { #dash > .app-well-app {

View File

@ -29,10 +29,20 @@ function AlphabeticalView() {
AlphabeticalView.prototype = { AlphabeticalView.prototype = {
_init: function() { _init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START }); this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
this._filterApp = null;
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
vshadows: true });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
}, },
_removeAll: function() { _removeAll: function() {
@ -40,20 +50,24 @@ AlphabeticalView.prototype = {
this._apps = []; this._apps = [];
}, },
_addApp: function(app) { _addApp: function(appInfo) {
let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id())); let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
appIcon.connect('launching', Lang.bind(this, function() {
this.emit('launching');
}));
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
this.emit('drag-begin');
}));
this._grid.addItem(appIcon.actor); this._grid.addItem(appIcon.actor);
appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon); this._apps.push(appIcon);
}, },
setFilter: function(filter) {
this._filterApp = filter;
for (let i = 0; i < this._apps.length; i++)
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
},
refresh: function(apps) { refresh: function(apps) {
let ids = []; let ids = [];
for (let i in apps) for (let i in apps)
@ -70,8 +84,6 @@ AlphabeticalView.prototype = {
} }
}; };
Signals.addSignalMethods(AlphabeticalView.prototype);
function ViewByCategories() { function ViewByCategories() {
this._init(); this._init();
} }
@ -79,59 +91,78 @@ function ViewByCategories() {
ViewByCategories.prototype = { ViewByCategories.prototype = {
_init: function() { _init: function() {
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this.actor = new St.BoxLayout({ vertical: true }); this.actor = new St.BoxLayout({ style_class: 'all-app' });
this.actor._delegate = this; this.actor._delegate = this;
this._view = new AlphabeticalView();
this._filters = new St.BoxLayout({ vertical: true });
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
this._sections = []; this._sections = [];
}, },
_updateSections: function(apps) { _selectCategory: function(num) {
this._removeAll(); if (num != -1)
this._allFilter.remove_style_pseudo_class('selected');
else
this._allFilter.add_style_pseudo_class('selected');
let sections = this._appSystem.get_sections(); this._view.setFilter(Lang.bind(this, function(app) {
if (!sections) if (num == -1)
return; return true;
for (let i = 0; i < sections.length; i++) { return this._sections[num].name == app.get_section();
if (i) { }));
let actor = new St.Bin({ style_class: 'app-section-divider' });
let divider = new St.Bin({ style_class: 'app-section-divider-container',
child: actor,
x_fill: true });
this.actor.add(divider, { y_fill: false, expand: true }); for (let i = 0; i < this._sections.length; i++) {
} if (i == num)
let _apps = apps.filter(function(app) { this._sections[i].filterActor.add_style_pseudo_class('selected');
return app.get_section() == sections[i]; else
}); this._sections[i].filterActor.remove_style_pseudo_class('selected');
this._sections[i] = { view: new AlphabeticalView(),
apps: _apps,
name: sections[i] };
this._sections[i].view.connect('launching', Lang.bind(this, function() {
this.emit('launching');
}));
this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
this.emit('drag-begin');
}));
this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
} }
}, },
_addFilter: function(name, num) {
let button = new St.Button({ label: name,
style_class: 'app-filter',
x_align: St.Align.START });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(num);
}));
if (num != -1)
this._sections[num] = { filterActor: button,
name: name };
else
this._allFilter = button;
},
_removeAll: function() { _removeAll: function() {
this.actor.destroy_children();
this._sections.forEach(function (section) { section.view.disconnectAll(); });
this._sections = []; this._sections = [];
this._filters.destroy_children();
}, },
refresh: function(apps) { refresh: function(apps) {
this._updateSections(apps); this._removeAll();
for (let i = 0; i < this._sections.length; i++) {
this._sections[i].view.refresh(this._sections[i].apps); let sections = this._appSystem.get_sections();
} this._apps = apps;
this._view.refresh(apps);
this._addFilter(_("All"), -1);
if (!sections)
return;
for (let i = 0; i < sections.length; i++)
this._addFilter(sections[i], i);
this._selectCategory(-1);
} }
}; };
Signals.addSignalMethods(ViewByCategories.prototype);
/* This class represents a display containing a collection of application items. /* This class represents a display containing a collection of application items.
* The applications are sorted based on their name. * The applications are sorted based on their name.
*/ */
@ -146,17 +177,8 @@ AllAppDisplay.prototype = {
Main.queueDeferredWork(this._workId); Main.queueDeferredWork(this._workId);
})); }));
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
vshadows: true });
this.actor = new St.Bin({ style_class: 'all-app',
y_align: St.Align.START,
child: this._scrollView });
this._appView = new ViewByCategories(); this._appView = new ViewByCategories();
this._scrollView.add_actor(this._appView.actor); this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay)); this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
}, },
@ -169,8 +191,6 @@ AllAppDisplay.prototype = {
this._appView.refresh(apps); this._appView.refresh(apps);
} }
}; };
Signals.addSignalMethods(AllAppDisplay.prototype);
function BaseAppSearchProvider() { function BaseAppSearchProvider() {
this._init(); this._init();