Compare commits

...

24 Commits

Author SHA1 Message Date
Jasper St. Pierre
9efa271888 Implement Sent signals for Telepathy.
This will allow us to update our notifications when
someone uses a different Telepathy client to send
messages.

https://bugzilla.gnome.org/show_bug.cgi?id=635991
2010-11-28 10:28:51 -05:00
Jasper St. Pierre
9ed3ce9006 Add a search provider for the session's current windows.
https://bugzilla.gnome.org/show_bug.cgi?id=635989
2010-11-28 10:26:09 -05:00
Florian Müllner
b400fc2837 workspace-indicators: Add hover indication
Scale up indicators on hover to hint at their clickability.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
Florian Müllner
a66af6fbac overview: Update animation
Update the animation on entering/leaving the overview to only zoom
the window previews and fade other elements.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
Florian Müllner
1bbc138ed2 dash: Improve DND to dash and allow reordering
Show a positional indicator where a new favorite will be added and
make the favorites re-orderable. Also allow the removal of favorites
using drag-and-drop according to the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
Florian Müllner
868d4756e4 workspaces: Change handling of window-drag signals
Delegate the emission of the window-drag-begin/window-drag-end
signals to overview functions, as done already for other items.
This will enable objects to react to those signals without having
access to the workspace objects / the workspaces view.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
Florian Müllner
dfa3be59dc search-display: Change the default display to use iconGrid
Current mockups display all search results as icons as used by
application results, so change the default result display to use
iconGrid/BaseIcon. Remove the custom application results display,
as it is no longer needed.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
Florian Müllner
62677336a5 overview: Add ViewSelector to the overview
Add the view selector and adjust the positioning of elements in the
overview. Unlike the old dash, the view selector is made public to
indicate that extensions may add additional views or search providers.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
264a82cbfc workspaces: Rework workspace controls for the view selector
As workspaces will appear as a particular view in the view selector,
merge WorkspacesControls and WorkspacesManager to control workspaces
and related controls, so that a single actor can be added to the
selector instead of positioning the elements from the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
f8cfd9f903 workspaces-view: Swap workspace ordering for RTL locales
Make the first workspace the right-most one in RTL locales, as one
would expect. Update all dragging/scrolling functions to behave
correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
13f8cd3bcf workspaces-view: Remove MosaicView
The new layout does no longer support view switching, so merge
GenericWorkspacesView and SingleView, and remove MosaicView.
Also rename or remove workspace properties and functions which
are now unused.
The grid will have a comeback with the new DND behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
e1f336374b app-display: Slight cleanup and style update
Being no longer an independent menu pane, both the toggle() and
close() functions are no longer needed, and the view's structure
can be simplified a bit.

Also update the style to fit into the view selector.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
0ba1387d19 Fake workspaces tab
https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
8832ce3a2b view-selector: Add keyboard shortcut for view switching
As the view selector is a tabbed interface, use the default keyboard
shortcut of Ctrl-PageUp/PageDown of GtkNotebook for switching between
views.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
Florian Müllner
91d7a022a9 view-selector: Move search logic into SearchTab
The view selector should only deal with view switching, so move the
logic to deal with search (find-as-you-type, cancelling a search,
navigating/activating results) into the SearchTab.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:03 +01:00
Florian Müllner
95d15725fc Use the old dash code to implement the view selector
The view selector is a tabbed interface with a search entry. Starting
a search switches focus to the results' tab, ending a search moves the
focus back to the previously selected tab. Activating a normal tab
while a search is active cancels the search.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:10:21 +01:00
Florian Müllner
bb2b7d83af search-display: Move SearchResults to a separate file
With the new layout, search results will be displayed in an independent
view like window previews, applications and possible future additions;
it does not make much sense keeping it with the switching logic, so move
the code to its own file.

Also remove the dash-prefix from the relevant style classes.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
Florian Müllner
0abac6f67d dash: Move padding into the icon for Fittsability
With this change, the icons' reactive area extends to the screen
edge, making them good targets according to Fitts' law.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
Florian Müllner
e00398e2ac dash: Reimplement the dash based on AppWell code
The new dash implementation is a single-column vertical sidebar,
whose items are scaled dynamically to fit the available height.
If the height is still exceeded after scaling down to a minimum
item size, excess items are cut off.
The now unused old dash implementation is renamed to OldDash, as
its code will be used as a base for the new view selector element.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
Florian Müllner
0f19492545 linear-view: Remove NewWorkspaceArea
As the button to add workspaces will move to the same position as
the new workspace drop area in drag mode, the latter is redundant
and can be removed.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
Florian Müllner
9d46a6ceea linear-view: Remove shadows when zoomed out
Overlaying inactive workspaces with a gradient to fade out the actors
does no longer work when re-using the normal desktop background. If
we keep the current DND behavior, we probably want to implement a real
fade effect - for now, just remove the visually disruptive shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
Florian Müllner
6432de95cc overview: Do not zoom the desktop background
While scaling the desktop background with the window previews represents
workspaces quite intuitively, the approach is not without problems.
As window previews in the overview behave quite differently to "real"
windows, the representation of workspaces as miniature versions of
"real" workspaces is flawed. The scaling also makes the transitions
to and from the overview much more visually expensive, without adding
much benefit.
Leaving the background in place provides more visual stability to the
transitions and emphasizes the distinctive behavior of elements in the
overview.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:46 +01:00
Florian Müllner
738fc375c0 overview: Replace InfoBar with message tray notifications
The layout of recent mockups occupies the space previously reserved
for the info bar with the view selector. As the bar's purpose is
mainly to provide the user with feedback, it makes sense to use the
existing message tray facility instead of moving the bar elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-18 20:35:55 +01:00
Florian Müllner
4e9a530a64 linear-view: Remove the scrollbar
The scrollbar is the main culprit for cluttered controls in the
linear view - all its functionality is already provided by the
workspace indicators, so it is save to remove the scrollbar in
order to clean up the interface.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-18 20:35:55 +01:00
19 changed files with 2354 additions and 2838 deletions

View File

@ -25,6 +25,7 @@ dist_theme_DATA = \
theme/close-window.svg \ theme/close-window.svg \
theme/close.svg \ theme/close.svg \
theme/corner-ripple.png \ theme/corner-ripple.png \
theme/dash-placeholder.svg \
theme/dialog-error.svg \ theme/dialog-error.svg \
theme/gnome-shell.css \ theme/gnome-shell.css \
theme/mosaic-view-active.svg \ theme/mosaic-view-active.svg \
@ -32,6 +33,7 @@ dist_theme_DATA = \
theme/move-window-on-new.svg \ theme/move-window-on-new.svg \
theme/process-working.png \ theme/process-working.png \
theme/remove-workspace.svg \ theme/remove-workspace.svg \
theme/running-indicator.svg \
theme/scroll-button-down-hover.png \ theme/scroll-button-down-hover.png \
theme/scroll-button-down.png \ theme/scroll-button-down.png \
theme/scroll-button-up-hover.png \ theme/scroll-button-up-hover.png \

View File

@ -41,18 +41,6 @@
<default>[]</default> <default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary> <_summary>History for command (Alt-F2) dialog</_summary>
</key> </key>
<key name="workspaces-view" type="s">
<default>'single'</default>
<_summary>Overview workspace view mode</_summary>
<_description>
The selected workspace view mode in the overview.
Supported values are "single" and "grid".
</_description>
<choices>
<choice value="single"/>
<choice value="grid"/>
</choices>
</key>
<child name="clock" schema="org.gnome.shell.clock"/> <child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/> <child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/> <child name="recorder" schema="org.gnome.shell.recorder"/>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="76"
height="27"
id="svg11252"
version="1.1">
<defs
id="defs11254">
<radialGradient
xlink:href="#linearGradient39563-4-2"
id="radialGradient68155-2-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.3486842,0,317.8421)"
cx="49"
cy="488"
fx="49"
fy="488"
r="38" />
<linearGradient
id="linearGradient39563-4-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop39565-1-4" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop39567-7-9" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient39573-6-1"
id="radialGradient68157-0-8"
gradientUnits="userSpaceOnUse"
cx="50.5"
cy="487.5"
fx="50.5"
fy="487.5"
r="10.5" />
<linearGradient
id="linearGradient39573-6-1">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop39575-5-6" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop39577-1-2" />
</linearGradient>
</defs>
<g
id="layer1"
transform="translate(-337,-518.86218)">
<g
id="g99967"
style="display:inline"
transform="translate(326,44.862171)">
<rect
style="opacity:0.49375;color:#000000;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect99969"
width="76"
height="2"
x="11"
y="487"
rx="0"
ry="0" />
<path
style="opacity:0.43125;color:#000000;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path99971"
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
transform="matrix(1.2857143,0,0,1.2857143,-14.428572,-139.28571)" />
<path
transform="matrix(0.43589747,0,0,0.43589747,28.487179,275)"
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
id="path99973"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -257,25 +257,9 @@ StTooltip StLabel {
/* Overview */ /* Overview */
.overview { #overview {
background-color: #111; spacing: 12px;
} background-color: rgba(0,0,0,0.6);
.info-bar {
color: #fff;
font-size: 14px;
spacing: 20px;
}
.info-bar-link-button {
background-color: #2d2d2d;
padding: 2px 14px;
border-radius: 10px;
border: 1px solid #181818;
}
.info-bar-link-button:hover {
border: 1px solid #666666;
} }
.new-workspace-area { .new-workspace-area {
@ -323,18 +307,17 @@ StTooltip StLabel {
} }
.workspaces-bar { .workspaces-bar {
height: 48px; spacing: 5px;
} }
.workspaces-bar { .workspace-indicator-panel {
spacing: 5px; spacing: 8px;
} }
.workspace-indicator { .workspace-indicator {
width: 24px; width: 24px;
height: 16px; height: 16px;
background: rgba(155,155,155,0.8); background: rgba(255,255,255,0.2);
border-spacing: 16px;
} }
.workspace-indicator.active { .workspace-indicator.active {
@ -359,44 +342,33 @@ StTooltip StLabel {
} }
.single-view-controls { .single-view-controls {
padding: 0px 15px; padding: 8px 0px;
} }
.workspace-controls { .workspace-controls {
width: 24px; width: 48px;
height: 16px; font-size: 32px;
font-weight: bold;
color: #ffffff;
border: 2px solid rgba(128, 128, 128, 0.4);
border-right: 0px;
border-radius: 9px 0px 0px 9px;
} }
.workspace-controls.add { .add-workspace {
background-image: url("add-workspace.svg"); background-color: rgba(128, 128, 128, 0.4);
} }
.workspace-controls.remove { .add-workspace:hover {
background-image: url("remove-workspace.svg"); background-color: rgba(128, 128, 128, 0.6);
} }
.workspace-controls.switch-single { .remove-workspace {
background-image: url("single-view.svg"); height: 48px;
} }
.workspace-controls.switch-mosaic { .remove-workspace:hover {
background-image: url("mosaic-view.svg"); background-color: rgba(128, 128, 128, 0.2);
}
.workspace-controls.switch-single:checked {
background-image: url("single-view-active.svg");
}
.workspace-controls.switch-mosaic:checked {
background-image: url("mosaic-view-active.svg");
}
#SwitchScroll {
height: 14px;
}
#SwitchScroll #hhandle {
border-radius: 7px;
} }
/* Dash */ /* Dash */
@ -404,115 +376,111 @@ StTooltip StLabel {
#dash { #dash {
color: #5f5f5f; color: #5f5f5f;
font-size: 12px; font-size: 12px;
padding: 0px 14px; padding: 6px 0px;
background-color: rgba(0, 0, 0, 0.5);
border: 2px solid rgba(128, 128, 128, 0.4);
border-left: 0px;
border-radius: 0px 9px 9px 0px;
} }
#dashSections { .dash-placeholder {
spacing: 12px; background-image: url("dash-placeholder.svg");
height: 27px;
}
#viewSelector {
spacing: 16px;
}
#searchArea {
padding: 0px 12px;
} }
#searchEntry { #searchEntry {
padding: 4px; padding: 4px 8px;
border-radius: 4px; border-radius: 12px;
color: #a8a8a8; color: rgb(128, 128, 128);
border: 1px solid #565656; border: 2px solid rgba(128, 128, 128, 0.4);
background-color: #404040; background-gradient-start: rgba(0, 0, 0, 0.2);
caret-color: #fff; background-gradient-end: rgba(128, 128, 128, 0.2);
background-gradient-direction: vertical;
caret-color: rgb(128, 128, 128);
caret-size: 1px; caret-size: 1px;
height: 16px; height: 16px;
width: 250px;
transition-duration: 300; transition-duration: 300;
} }
#searchEntry:focus { #searchEntry:focus {
color: #545454; border: 2px solid #ffffff;
border: 1px solid #3a3a3a; background-gradient-start: rgba(0, 0, 0, 0.2);
background-color: #e8e8e8; background-gradient-end: #ffffff;
caret-color: #545454; background-gradient-direction: vertical;
color: rgb(64, 64, 64);
font-weight: bold;
-st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9); -st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
transition-duration: 0; transition-duration: 0;
} }
#searchEntry:hover { #searchEntry:hover {
color: #a8a8a8; border: 2px solid #e8e8e8;
border: 1px solid #4d4d4d;
background-color: #e8e8e8;
caret-color: #545454; caret-color: #545454;
transition-duration: 500; transition-duration: 500;
} }
.dash-section { .view-tab-title {
color: #888a85;
font-weight: bold;
padding: 0px 12px;
}
.view-tab-title:selected {
color: white;
}
.view-tab-boxpointer {
-arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.5);
-arrow-border-width: 2px;
-arrow-border-color: rgba(255,255,255,0.5);
-arrow-base: 30px;
-arrow-rise: 15px;
}
#searchResults {
padding: 20px 10px 10px 10px;
}
#searchResultsContent {
padding: 0 10px;
spacing: 8px; spacing: 8px;
} }
.section-header { .search-statustext,
} .search-section-header {
padding: 4px 12px;
.section-header-inner {
spacing: 4px; spacing: 4px;
color: #6f6f6f;
} }
.section-text-content { .search-section {
padding: 4px 0px; background-color: rgba(128, 128, 128, .1);
} border: 1px solid rgba(50, 50, 50, .4);
.dash-section-content {
color: #ffffff;
spacing: 8px;
}
.more-link {
}
.more-link-expander {
background-image: url("section-more.svg");
width: 9px;
height: 9px;
}
.more-link-expander.open {
background-image: url("section-more-open.svg");
width: 9px;
height: 9px;
}
.dash-pane {
border-radius: 10px; border-radius: 10px;
background-color: #111111; }
border: 2px solid #868686;
.search-section-results {
color: #ffffff; color: #ffffff;
padding: 30px 10px 10px 20px; border-radius: 10px;
border: 1px solid rgba(50, 50, 50, .4);
padding: 6px;
} }
#dashAppSearchResults { .search-section-list-results {
padding: 8px 0px;
}
.dash-search-statustext,
.dash-search-section-header {
padding: 4px 0px;
spacing: 4px; spacing: 4px;
} }
.dash-search-section-results { .results-container {
color: #ffffff;
}
.dash-search-section-list-results {
spacing: 4px;
}
.dash-search-result-content {
padding: 3px;
}
.dash-search-result-content:selected {
padding: 2px;
border: 1px solid #5c5c5c;
border-radius: 2px;
background-color: #1e1e1e;
}
.dash-results-container {
spacing: 4px; spacing: 4px;
} }
@ -564,10 +532,7 @@ StTooltip StLabel {
} }
.all-app { .all-app {
border-radius: 10px; padding: 10px;
background-color: #111111;
border: 2px solid #868686;
color: #ffffff;
} }
.app-section-divider-container { .app-section-divider-container {
@ -580,46 +545,48 @@ StTooltip StLabel {
background-image: url("separator-white.png"); background-image: url("separator-white.png");
} }
.all-app-controls-panel { #dash > .app-well-app {
height: 30px; padding: 6px 12px;
} }
.all-app-scroll-view { .remove-favorite-icon {
padding-right: 10px; color: #a0a0a0;
padding-left: 10px;
padding-bottom: 10px;
} }
.app-well-app { .remove-favorite-icon:hover {
border: 1px solid #181818; color: white;
}
.app-well-app > .overview-icon,
.remove-favorite > .overview-icon,
.search-result-content > .overview-icon {
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
width: 70px;
height: 70px;
font-size: 10px; font-size: 10px;
color: white;
transition-duration: 100; transition-duration: 100;
text-align: center; text-align: center;
} }
.app-well-app.running { .app-well-app.running > .overview-icon {
background-gradient-direction: vertical; text-shadow: black 0px 2px 2px;
background-gradient-start: #3d3d3d; background-image: url("running-indicator.svg");
background-gradient-end: #181818;
} }
.app-well-app.selected { .app-well-app:selected > .overview-icon,
border: 1px solid #666666; .search-result-content:selected > .overview-icon {
background: rgba(255,255,255,0.33);
} }
.app-well-app:hover { .app-well-app:hover > .overview-icon,
border: 1px solid #666666; .remove-favorite:hover > .overview-icon,
background-gradient-direction: vertical; .search-result-content:hover > .overview-icon {
background-gradient-start: rgba(61,61,61,0.8); background: rgba(255,255,255,0.33);
background-gradient-end: rgba(24,24,24,0.2); text-shadow: black 0px 2px 2px;
transition-duration: 100; transition-duration: 100;
} }
.app-well-app:active { .app-well-app:active > .overview-icon {
background-color: #1e1e1e; background-color: #1e1e1e;
border: 1px solid #5f5f5f; border: 1px solid #5f5f5f;
} }

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="74.01342"
height="74.006706"
id="svg7355"
version="1.1">
<defs
id="defs7357">
<radialGradient
xlink:href="#linearGradient36429"
id="radialGradient7461"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
cx="47.878681"
cy="171.25"
fx="47.878681"
fy="171.25"
r="37" />
<linearGradient
id="linearGradient36429">
<stop
id="stop36431"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop36433"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient36471"
id="radialGradient7463"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
cx="49.067139"
cy="242.50381"
fx="49.067139"
fy="242.50381"
r="37.00671" />
<linearGradient
id="linearGradient36471">
<stop
id="stop36473"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop36475"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<radialGradient
r="37.00671"
fy="242.50381"
fx="49.067139"
cy="242.50381"
cx="49.067139"
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
gradientUnits="userSpaceOnUse"
id="radialGradient7488"
xlink:href="#linearGradient36471" />
</defs>
<g
id="layer1"
transform="translate(-266.21629,-168.11809)">
<g
style="display:inline"
id="g30864"
transform="translate(255.223,70.118091)">
<rect
ry="3.5996203"
rx="3.5996203"
y="98"
x="11"
height="74"
width="74"
id="rect14000"
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
<path
id="rect34520"
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -39,6 +39,7 @@ nobase_dist_js_DATA = \
ui/runDialog.js \ ui/runDialog.js \
ui/scripting.js \ ui/scripting.js \
ui/search.js \ ui/search.js \
ui/searchDisplay.js \
ui/shellDBus.js \ ui/shellDBus.js \
ui/statusIconDispatcher.js \ ui/statusIconDispatcher.js \
ui/statusMenu.js \ ui/statusMenu.js \
@ -47,6 +48,7 @@ nobase_dist_js_DATA = \
ui/status/volume.js \ ui/status/volume.js \
ui/telepathyClient.js \ ui/telepathyClient.js \
ui/tweener.js \ ui/tweener.js \
ui/viewSelector.js \
ui/windowAttentionHandler.js \ ui/windowAttentionHandler.js \
ui/windowManager.js \ ui/windowManager.js \
ui/workspace.js \ ui/workspace.js \

View File

@ -272,7 +272,9 @@ const ChannelTextIface = {
], ],
signals: [ signals: [
{ name: 'Received', { name: 'Received',
inSignature: 'uuuuus' } inSignature: 'uuuuus' },
{ name: 'Sent',
inSignature: 'uus' }
] ]
}; };
let ChannelText = DBus.makeProxyClass(ChannelTextIface); let ChannelText = DBus.makeProxyClass(ChannelTextIface);

View File

@ -21,8 +21,6 @@ const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
const Params = imports.misc.params; const Params = imports.misc.params;
const WELL_MAX_COLUMNS = 16;
const WELL_MAX_SEARCH_ROWS = 1;
const MENU_POPUP_TIMEOUT = 600; const MENU_POPUP_TIMEOUT = 600;
function AlphabeticalView() { function AlphabeticalView() {
@ -148,22 +146,14 @@ AllAppDisplay.prototype = {
Main.queueDeferredWork(this._workId); Main.queueDeferredWork(this._workId);
})); }));
let bin = new St.BoxLayout({ style_class: 'all-app-controls-panel', this._scrollView = new St.ScrollView({ x_fill: true,
reactive: true });
this.actor = new St.BoxLayout({ style_class: 'all-app', vertical: true });
this.actor.hide();
let view = new St.ScrollView({ x_fill: true,
y_fill: false, y_fill: false,
style_class: 'all-app-scroll-view',
vshadows: true }); vshadows: true });
this._scrollView = view; this.actor = new St.Bin({ style_class: 'all-app',
this.actor.add(bin); y_align: St.Align.START,
this.actor.add(view, { expand: true, y_fill: false, y_align: St.Align.START }); child: this._scrollView });
this._appView = new ViewByCategories(); this._appView = new ViewByCategories();
this._appView.connect('launching', Lang.bind(this, this.close));
this._appView.connect('drag-begin', Lang.bind(this, this.close));
this._scrollView.add_actor(this._appView.actor); this._scrollView.add_actor(this._appView.actor);
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
@ -177,101 +167,10 @@ AllAppDisplay.prototype = {
}); });
this._appView.refresh(apps); this._appView.refresh(apps);
},
toggle: function() {
if (this.actor.visible) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: Overview.PANE_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.hide();
this.emit('open-state-changed',
this.actor.visible);
})
});
} else {
this.actor.show();
this.emit('open-state-changed', this.actor.visible);
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: Overview.PANE_FADE_TIME,
transition: 'easeOutQuad'
});
}
},
close: function() {
if (!this.actor.visible)
return;
this.toggle();
} }
}; };
Signals.addSignalMethods(AllAppDisplay.prototype); Signals.addSignalMethods(AllAppDisplay.prototype);
function AppSearchResultDisplay(provider) {
this._init(provider);
}
AppSearchResultDisplay.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function (provider) {
Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = new IconGrid.IconGrid({ rowLimit: WELL_MAX_SEARCH_ROWS });
this.actor = new St.Bin({ name: 'dashAppSearchResults',
x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
},
renderResults: function(results, terms) {
let appSys = Shell.AppSystem.get_default();
let maxItems = WELL_MAX_SEARCH_ROWS * WELL_MAX_COLUMNS;
for (let i = 0; i < results.length && i < maxItems; i++) {
let result = results[i];
let app = appSys.get_app(result);
let display = new AppWellIcon(app);
this._grid.addItem(display.actor);
}
},
clear: function () {
this._grid.removeAll();
this.selectionIndex = -1;
},
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
},
selectIndex: function (index) {
let nVisible = this.getVisibleResultCount();
if (this.selectionIndex >= 0) {
let prevActor = this._grid.getItemAtIndex(this.selectionIndex);
prevActor._delegate.setSelected(false);
}
this.selectionIndex = -1;
if (index >= nVisible)
return false;
else if (index < 0)
return false;
let targetActor = this._grid.getItemAtIndex(index);
targetActor._delegate.setSelected(true);
this.selectionIndex = index;
return true;
},
activateSelected: function() {
if (this.selectionIndex < 0)
return;
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
this.provider.activateResult(targetActor._delegate.app.get_id());
}
};
function BaseAppSearchProvider() { function BaseAppSearchProvider() {
this._init(); this._init();
@ -324,12 +223,10 @@ AppSearchProvider.prototype = {
return this._appSys.subsearch(false, previousResults, terms); return this._appSys.subsearch(false, previousResults, terms);
}, },
createResultContainerActor: function () {
return new AppSearchResultDisplay(this);
},
createResultActor: function (resultMeta, terms) { createResultActor: function (resultMeta, terms) {
return new AppIcon(resultMeta.id); let app = this._appSys.get_app(resultMeta['id']);
let icon = new AppWellIcon(app);
return icon.actor;
}, },
expandSearch: function(terms) { expandSearch: function(terms) {
@ -375,7 +272,9 @@ AppIcon.prototype = {
let label = this.app.get_name(); let label = this.app.get_name();
IconGrid.BaseIcon.prototype._init.call(this, label); IconGrid.BaseIcon.prototype._init.call(this,
label,
{ setSizeManually: true });
}, },
createIcon: function(iconSize) { createIcon: function(iconSize) {
@ -396,8 +295,8 @@ AppWellIcon.prototype = {
y_fill: true }); y_fill: true });
this.actor._delegate = this; this.actor._delegate = this;
this._icon = new AppIcon(app); this.icon = new AppIcon(app);
this.actor.set_child(this._icon.actor); this.actor.set_child(this.icon.actor);
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
@ -526,14 +425,6 @@ AppWellIcon.prototype = {
} }
}, },
setSelected: function (isSelected) {
this._selected = isSelected;
if (this._selected)
this.actor.add_style_class_name('selected');
else
this.actor.remove_style_class_name('selected');
},
_onMenuPoppedUp: function() { _onMenuPoppedUp: function() {
if (this._getRunning()) { if (this._getRunning()) {
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id()); Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
@ -581,13 +472,13 @@ AppWellIcon.prototype = {
}, },
getDragActor: function() { getDragActor: function() {
return this.app.create_icon_texture(this._icon.iconSize); return this.app.create_icon_texture(this.icon.iconSize);
}, },
// Returns the original actor that should align with the actor // Returns the original actor that should align with the actor
// we show as the item is being dragged. // we show as the item is being dragged.
getDragActorSource: function() { getDragActorSource: function() {
return this._icon.icon; return this.icon.icon;
} }
}; };
Signals.addSignalMethods(AppWellIcon.prototype); Signals.addSignalMethods(AppWellIcon.prototype);
@ -756,135 +647,3 @@ AppIconMenu.prototype = {
} }
}; };
Signals.addSignalMethods(AppIconMenu.prototype); Signals.addSignalMethods(AppIconMenu.prototype);
function AppWell() {
this._init();
}
AppWell.prototype = {
_init : function() {
this._placeholderText = null;
this._menus = [];
this._menuDisplays = [];
this._favorites = [];
this._grid = new IconGrid.IconGrid();
this.actor = this._grid.actor;
this.actor._delegate = this;
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
this._tracker = Shell.WindowTracker.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
},
_appIdListToHash: function(apps) {
let ids = {};
for (let i = 0; i < apps.length; i++)
ids[apps[i].get_id()] = apps[i];
return ids;
},
_queueRedisplay: function () {
Main.queueDeferredWork(this._workId);
},
_redisplay: function () {
this._grid.removeAll();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = '';
let running = this._tracker.get_running_apps(contextId);
let runningIds = this._appIdListToHash(running);
let nFavorites = 0;
for (let id in favorites) {
let app = favorites[id];
let display = new AppWellIcon(app);
this._grid.addItem(display.actor);
nFavorites++;
}
for (let i = 0; i < running.length; i++) {
let app = running[i];
if (app.get_id() in favorites)
continue;
let display = new AppWellIcon(app);
this._grid.addItem(display.actor);
}
if (this._placeholderText) {
this._placeholderText.destroy();
this._placeholderText = null;
}
if (running.length == 0 && nFavorites == 0) {
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
this.actor.add_actor(this._placeholderText);
}
},
handleDragOver : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppWellIcon)
app = this._appSystem.get_app(source.getId());
else if (source instanceof Workspace.WindowClone)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_transient())
return DND.DragMotionResult.NO_DROP;
let id = app.get_id();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
if (srcIsFavorite)
return DND.DragMotionResult.NO_DROP;
return DND.DragMotionResult.COPY_DROP;
},
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppWellIcon) {
app = this._appSystem.get_app(source.getId());
} else if (source instanceof Workspace.WindowClone) {
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_transient()) {
return false;
}
let id = app.get_id();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
if (srcIsFavorite) {
return false;
} else {
Mainloop.idle_add(Lang.bind(this, function () {
AppFavorites.getAppFavorites().addFavorite(id);
return false;
}));
}
return true;
}
};
Signals.addSignalMethods(AppWell.prototype);

View File

@ -63,7 +63,7 @@ AppFavorites.prototype = {
return appId in this._favorites; return appId in this._favorites;
}, },
_addFavorite: function(appId) { _addFavorite: function(appId, pos) {
if (appId in this._favorites) if (appId in this._favorites)
return false; return false;
@ -73,23 +73,35 @@ AppFavorites.prototype = {
return false; return false;
let ids = this._getIds(); let ids = this._getIds();
if (pos == -1)
ids.push(appId); ids.push(appId);
else
ids.splice(pos, 0, appId);
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
this._favorites[appId] = app; this._favorites[appId] = app;
return true; return true;
}, },
addFavorite: function(appId) { addFavoriteAtPos: function(appId, pos) {
if (!this._addFavorite(appId)) if (!this._addFavorite(appId, pos))
return; return;
let app = Shell.AppSystem.get_default().get_app(appId); let app = Shell.AppSystem.get_default().get_app(appId);
Main.overview.infoBar.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () { Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId); this._removeFavorite(appId);
})); }));
}, },
addFavorite: function(appId) {
this.addFavoriteAtPos(appId, -1);
},
moveFavoriteToPos: function(appId, pos) {
this._removeFavorite(appId);
this._addFavorite(appId, pos);
},
_removeFavorite: function(appId) { _removeFavorite: function(appId) {
if (!appId in this._favorites) if (!appId in this._favorites)
return false; return false;
@ -100,13 +112,16 @@ AppFavorites.prototype = {
}, },
removeFavorite: function(appId) { removeFavorite: function(appId) {
let ids = this._getIds();
let pos = ids.indexOf(appId);
let app = this._favorites[appId]; let app = this._favorites[appId];
if (!this._removeFavorite(appId)) if (!this._removeFavorite(appId))
return; return;
Main.overview.infoBar.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () { Lang.bind(this, function () {
this._addFavorite(appId); this._addFavorite(appId, pos);
})); }));
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
const Lang = imports.lang; const Lang = imports.lang;
@ -8,104 +9,60 @@ const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell'); const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext; const _ = Gettext.gettext;
const AppDisplay = imports.ui.appDisplay;
const Dash = imports.ui.dash;
const DocDisplay = imports.ui.docDisplay;
const GenericDisplay = imports.ui.genericDisplay; const GenericDisplay = imports.ui.genericDisplay;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Dash = imports.ui.dash; const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspacesView = imports.ui.workspacesView; const WorkspacesView = imports.ui.workspacesView;
// Time for initial animation going into Overview mode // Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25; const ANIMATION_TIME = 0.25;
// Time for pane menus to fade in/out // We split the screen vertically between the dash and the view selector.
const PANE_FADE_TIME = 0.1; const DASH_SPLIT_FRACTION = 0.1;
// We divide the screen into a grid of rows and columns, which we use const SHELL_INFO_HIDE_TIMEOUT = 10;
// to help us position the Overview components, such as the side panel
// that lists applications and documents, the workspaces display, and
// the button for adding additional workspaces.
// In the regular mode, the side panel takes up one column on the left,
// and the workspaces display takes up the remaining columns.
// In the expanded side panel display mode, the side panel takes up two
// columns, and the workspaces display slides all the way to the right,
// being visible only in the last quarter of the right-most column.
// In the future, this mode will have more components, such as a display
// of documents which were recently opened with a given application, which
// will take up the remaining sections of the display.
const WIDE_SCREEN_CUT_OFF_RATIO = 1.4; function Source() {
// A common netbook resolution is 1024x600, which trips the widescreen
// ratio. However that leaves way too few pixels for the dash. So
// just treat this as a regular screen.
const WIDE_SCREEN_MINIMUM_HEIGHT = 768;
const COLUMNS_REGULAR_SCREEN = 4;
const ROWS_REGULAR_SCREEN = 8;
const COLUMNS_WIDE_SCREEN = 5;
const ROWS_WIDE_SCREEN = 10;
const DEFAULT_PADDING = 4;
// Padding around workspace grid / Spacing between Dash and Workspaces
const WORKSPACE_GRID_PADDING = 12;
const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3;
const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6;
const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4;
const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8;
// A multi-state; PENDING is used during animations
const STATE_ACTIVE = true;
const STATE_PENDING_INACTIVE = false;
const STATE_INACTIVE = false;
const SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
const SHADOW_WIDTH = 6;
const NUMBER_OF_SECTIONS_IN_SEARCH = 2;
const INFO_BAR_HIDE_TIMEOUT = 10;
let wideScreen = false;
let displayGridColumnWidth = null;
let displayGridRowHeight = null;
function InfoBar() {
this._init(); this._init();
} }
InfoBar.prototype = { Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function() { _init: function() {
this.actor = new St.Bin({ style_class: 'info-bar-panel', MessageTray.Source.prototype._init.call(this,
x_fill: true, "System Information");
y_fill: false }); this._setSummaryIcon(this.createNotificationIcon());
this._label = new St.Label(); },
this._undo = new St.Button({ style_class: 'info-bar-link-button' });
let bin = new St.Bin({ x_fill: false, createNotificationIcon: function() {
y_fill: false, return new St.Icon({ icon_name: 'info',
x_align: St.Align.MIDDLE, icon_type: St.IconType.FULLCOLOR,
y_align: St.Align.MIDDLE }); icon_size: this.ICON_SIZE });
this.actor.set_child(bin); },
let box = new St.BoxLayout({ style_class: 'info-bar' }); _notificationClicked: function() {
bin.set_child(box); this.destroy();
}
}
function ShellInfo() {
this._init();
}
ShellInfo.prototype = {
_init: function() {
this._source = null;
this._timeoutId = 0; this._timeoutId = 0;
box.add(this._label, {'y-fill' : false, 'y-align' : St.Align.MIDDLE});
box.add(this._undo);
this.actor.set_opacity(0);
this._undoCallback = null; this._undoCallback = null;
this._undo.connect('clicked', Lang.bind(this, this._onUndoClicked));
}, },
_onUndoClicked: function() { _onUndoClicked: function() {
@ -114,27 +71,16 @@ InfoBar.prototype = {
if (this._undoCallback) if (this._undoCallback)
this._undoCallback(); this._undoCallback();
this.actor.set_opacity(0);
this._undoCallback = null; this._undoCallback = null;
},
_hideDone: function() { if (this._source)
this._undoCallback = null; this._source.destroy();
},
_hide: function() {
Tweener.addTween(this.actor,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._hideDone,
onCompleteScope: this
});
}, },
_onTimeout: function() { _onTimeout: function() {
this._timeoutId = 0; this._timeoutId = 0;
this._hide(); if (this._source)
this._source.destroy();
return false; return false;
}, },
@ -142,28 +88,33 @@ InfoBar.prototype = {
if (this._timeoutId) if (this._timeoutId)
Mainloop.source_remove(this._timeoutId); Mainloop.source_remove(this._timeoutId);
this._timeout = false; this._timeoutId = Mainloop.timeout_add_seconds(SHELL_INFO_HIDE_TIMEOUT,
Lang.bind(this, this._onTimeout));
this._label.text = text; if (this._source == null) {
this._source = new Source();
this._source.connect('destroy', Lang.bind(this,
function() {
this._source = null;
}));
Main.messageTray.add(this._source);
}
Tweener.addTween(this.actor, let notification = this._source.notification;
{ opacity: 255, if (notification == null)
transition: 'easeOutQuad', notification = new MessageTray.Notification(this._source, text, null);
time: ANIMATION_TIME
});
this._timeoutId = Mainloop.timeout_add_seconds(INFO_BAR_HIDE_TIMEOUT, Lang.bind(this, this._onTimeout));
if (undoLabel)
this._undo.label = undoLabel;
else else
this._undo.label = _("Undo"); notification.update(text, null, { clear: true });
this._undoCallback = undoCallback; this._undoCallback = undoCallback;
if (undoCallback) if (undoCallback) {
this._undo.show(); notification.addButton('system-undo',
else undoLabel ? undoLabel : _("Undo"));
this._undo.hide(); notification.connect('action-invoked',
Lang.bind(this, this._onUndoClicked));
}
this._source.notify(notification);
} }
}; };
@ -173,30 +124,37 @@ function Overview() {
Overview.prototype = { Overview.prototype = {
_init : function() { _init : function() {
this._group = new St.Group({ style_class: 'overview' }); this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
// The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
this._background.hide();
global.overlay_group.add_actor(this._background);
this._spacing = 0;
this._group = new St.Group({ name: 'overview' });
this._group._delegate = this; this._group._delegate = this;
this._group.connect('destroy', Lang.bind(this, this._group.connect('style-changed',
function() { Lang.bind(this, function() {
if (this._lightbox) { let node = this._group.get_theme_node();
this._lightbox.destroy(); let spacing = node.get_length('spacing');
this._lightbox = null; if (spacing != this._spacing) {
this._spacing = spacing;
this.relayout();
} }
})); }));
this.infoBar = new InfoBar(); this.shellInfo = new ShellInfo();
this._group.add_actor(this.infoBar.actor);
this._workspacesManager = null; this._workspacesDisplay = null;
this._lightbox = null;
this.visible = false; this.visible = false;
this.animationInProgress = false; this.animationInProgress = false;
this._hideInProgress = false; this._hideInProgress = false;
this._recalculateGridSizes();
this._activeDisplayPane = null;
// During transitions, we raise this to the top to avoid having the overview // During transitions, we raise this to the top to avoid having the overview
// area be reactive; it causes too many issues such as double clicks on // area be reactive; it causes too many issues such as double clicks on
// Dash elements, or mouseover handlers in the workspaces. // Dash elements, or mouseover handlers in the workspaces.
@ -205,196 +163,87 @@ Overview.prototype = {
this._group.add_actor(this._coverPane); this._group.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
// Similar to the cover pane but used for dialogs ("panes"); see the comments
// in addPane below.
this._transparentBackground = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._group.add_actor(this._transparentBackground);
// Background color for the Overview
this._backOver = new St.Label();
this._group.add_actor(this._backOver);
this._group.hide(); this._group.hide();
global.overlay_group.add_actor(this._group); global.overlay_group.add_actor(this._group);
this.viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this.viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor);
let appView = new AppDisplay.AllAppDisplay();
this.viewSelector.addViewTab("Applications", appView.actor);
// Default search providers
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this.viewSelector.addSearchProvider(new WorkspacesView.WindowSearchProvider());
// TODO - recalculate everything when desktop size changes // TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash(); this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor); this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this.viewSelector.constrainY);
// Container to hold popup pane chrome. this._dash.actor.add_constraint(this.viewSelector.constrainHeight);
this._paneContainer = new St.BoxLayout({ style_class: 'overview-pane' });
// Note here we explicitly don't set the paneContainer to be reactive yet; that's done
// inside the notify::visible handler on panes.
this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) {
this._activeDisplayPane.close();
return true;
}));
this._group.add_actor(this._paneContainer);
this._transparentBackground.lower_bottom();
this._paneContainer.hide();
this._coverPane.lower_bottom(); this._coverPane.lower_bottom();
this.workspaces = null; this.workspaces = null;
}, },
_onViewChanged: function() { _getDesktopClone: function() {
if (!this.visible) let windows = global.get_window_actors().filter(function(w) {
return; return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP;
});
if (windows.length == 0)
return null;
this.workspaces = this._workspacesManager.workspacesView; let clone = new Clutter.Clone({ source: windows[0].get_texture() });
clone.source.connect('destroy', Lang.bind(this, function() {
// Show new workspacesView clone.destroy();
this._group.add_actor(this.workspaces.actor); }));
this._workspacesBar.raise(this.workspaces.actor); return clone;
this._dash.actor.raise(this.workspaces.actor);
},
_recalculateGridSizes: function () {
let primary = global.get_primary_monitor();
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
if (wideScreen) {
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN);
displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN);
} else {
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN);
displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN);
}
}, },
relayout: function () { relayout: function () {
let primary = global.get_primary_monitor(); let primary = global.get_primary_monitor();
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
this._recalculateGridSizes(); let contentY = Panel.PANEL_HEIGHT;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._group.set_position(primary.x, primary.y); this._group.set_position(primary.x, primary.y);
this._group.set_size(primary.width, primary.height); this._group.set_size(primary.width, primary.height);
let contentY = Panel.PANEL_HEIGHT;
let contentHeight = primary.height - contentY;
this._coverPane.set_position(0, contentY); this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight); this._coverPane.set_size(primary.width, contentHeight);
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN; let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing;
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN; let viewHeight = contentHeight - 2 * this._spacing;
let viewY = contentY + this._spacing;
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed let viewX = rtl ? 0
- WORKSPACE_GRID_PADDING * 2; : Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing;
// We scale the vertical padding by (primary.height / primary.width)
// so that the workspace preserves its aspect ratio.
this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2);
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) { if (rtl) {
this._workspacesX = WORKSPACE_GRID_PADDING; this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else { } else {
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; dashX = 0;
} }
this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width)); this._dash.actor.set_x(dashX);
if (rtl) { this.viewSelector.actor.set_position(viewX, viewY);
this._dash.actor.set_position(primary.width - displayGridColumnWidth, contentY); this.viewSelector.actor.set_size(viewWidth, viewHeight);
} else {
this._dash.actor.set_position(0, contentY);
}
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
this._dash.searchArea.height = this._workspacesY - contentY;
this._dash.sectionArea.height = this._workspacesHeight;
this._dash.searchResults.actor.height = this._workspacesHeight;
this.infoBar.actor.set_position(displayGridColumnWidth, Panel.PANEL_HEIGHT);
this.infoBar.actor.set_size(primary.width - displayGridColumnWidth, this._workspacesY - Panel.PANEL_HEIGHT);
this.infoBar.actor.raise_top();
// place the 'Add Workspace' button in the bottom row of the grid
this._workspacesBarX = this._workspacesX;
this._workspacesBarWidth = this._workspacesWidth;
this._workspacesBarY = primary.height - displayGridRowHeight;
// The parent (this._group) is positioned at the top left of the primary monitor
// while this._backOver occupies the entire screen.
this._backOver.set_position(- primary.x, - primary.y);
this._backOver.set_size(global.screen_width, global.screen_height);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
this._workspacesY);
// Dynamic width
this._paneContainer.height = this._workspacesHeight;
if (rtl) {
this._paneContainer.connect('notify::width', Lang.bind(this, function (paneContainer) {
paneContainer.x = this._dash.actor.x - (DEFAULT_PADDING + paneContainer.width);
}));
}
this._transparentBackground.set_position(primary.x, primary.y);
this._transparentBackground.set_size(primary.width, primary.height);
},
addPane: function (pane, align) {
pane.actor.height = .9 * this._workspacesHeight;
this._paneContainer.add(pane.actor, { expand: true,
y_fill: false,
y_align: align });
// When a pane is displayed, we raise the transparent background to the top
// and connect to button-release-event on it, then raise the pane above that.
// The idea here is that clicking anywhere outside the pane should close it.
// When the active pane is closed, undo the effect.
let backgroundEventId = null;
pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
if (isOpen) {
this._activeDisplayPane = pane;
this._transparentBackground.raise_top();
this._paneContainer.raise_top();
this._paneContainer.show();
this._paneReady = false;
if (backgroundEventId != null)
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = this._transparentBackground.connect('captured-event', Lang.bind(this, function (actor, event) {
if (event.get_source() != this._transparentBackground)
return false;
if (event.type() == Clutter.EventType.BUTTON_PRESS)
this._paneReady = true;
if (event.type() == Clutter.EventType.BUTTON_RELEASE
&& this._paneReady)
this._activeDisplayPane.close();
return true;
}));
if (!this._lightbox)
this._lightbox = new Lightbox.Lightbox(this._group,
{ fadeTime: PANE_FADE_TIME });
this._lightbox.show();
this._lightbox.highlight(this._paneContainer);
} else if (pane == this._activeDisplayPane) {
this._activeDisplayPane = null;
if (backgroundEventId != null) {
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = null;
}
this._transparentBackground.lower_bottom();
this._paneContainer.hide();
this._lightbox.hide();
}
}));
}, },
//// Public methods //// //// Public methods ////
beginItemDrag: function(source) { beginItemDrag: function(source) {
// Close any active panes if @source is a GenericDisplayItem.
// This allows the user to place the item on any workspace.
if (source instanceof GenericDisplay.GenericDisplayItem)
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
this.emit('item-drag-begin'); this.emit('item-drag-begin');
}, },
@ -402,6 +251,14 @@ Overview.prototype = {
this.emit('item-drag-end'); this.emit('item-drag-end');
}, },
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
},
endWindowDrag: function(source) {
this.emit('window-drag-end');
},
// Returns the scale the Overview has when we just start zooming out // Returns the scale the Overview has when we just start zooming out
// to overview mode. That is, when just the active workspace is showing. // to overview mode. That is, when just the active workspace is showing.
getZoomedInScale : function() { getZoomedInScale : function() {
@ -419,49 +276,23 @@ Overview.prototype = {
// Returns the current scale of the Overview. // Returns the current scale of the Overview.
getScale : function() { getScale : function() {
return this._group.scaleX; return this.workspaces.actor.scaleX;
}, },
// Returns the current position of the Overview. // Returns the current position of the Overview.
getPosition : function() { getPosition : function() {
return [this._group.x, this._group.y]; return [this.workspaces.actor.x, this.workspaces.actor.y];
}, },
show : function() { show : function() {
if (this.visible) if (this.visible)
return; return;
if (!Main.pushModal(this._dash.actor)) if (!Main.pushModal(this.viewSelector.actor))
return; return;
this.visible = true; this.visible = true;
this.animationInProgress = true; this.animationInProgress = true;
this._dash.show();
/* TODO: make this stuff dynamic */
this._workspacesManager =
new WorkspacesView.WorkspacesManager(this._workspacesWidth,
this._workspacesHeight,
this._workspacesX,
this._workspacesY);
this._workspacesManager.connect('view-changed',
Lang.bind(this, this._onViewChanged));
this.workspaces = this._workspacesManager.workspacesView;
this._group.add_actor(this.workspaces.actor);
// The workspaces actor is as big as the screen, so we have to raise the dash above it
// for drag and drop to work. In the future we should fix the workspaces to not
// be as big as the screen.
this._dash.actor.raise(this.workspaces.actor);
this._workspacesBar = this._workspacesManager.controlsBar.actor;
this._workspacesBar.set_position(this._workspacesBarX,
this._workspacesBarY);
this._workspacesBar.width = this._workspacesBarWidth;
this._group.add_actor(this._workspacesBar);
this._workspacesBar.raise(this.workspaces.actor);
// All the the actors in the window group are completely obscured, // All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly // hiding the group holding them while the Overview is displayed greatly
// increases performance of the Overview especially when there are many // increases performance of the Overview especially when there are many
@ -471,17 +302,38 @@ Overview.prototype = {
// clones of them, this would obviously no longer be necessary. // clones of them, this would obviously no longer be necessary.
global.window_group.hide(); global.window_group.hide();
this._group.show(); this._group.show();
this._background.show();
// Create a zoom out effect. First scale the Overview group up and this.viewSelector.show();
this._workspacesDisplay.show();
this._dash.show();
this.workspaces = this._workspacesDisplay.workspacesView;
global.overlay_group.add_actor(this.workspaces.actor);
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
// Create a zoom out effect. First scale the workspaces view up and
// position it so that the active workspace fills up the whole screen, // position it so that the active workspace fills up the whole screen,
// then transform the group to its normal dimensions and position. // then transform it to its normal dimensions and position.
// The opposite transition is used in hide(). // The opposite transition is used in hide().
this._group.scaleX = this._group.scaleY = this.getZoomedInScale(); this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale();
[this._group.x, this._group.y] = this.getZoomedInPosition(); [this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition();
let primary = global.get_primary_monitor(); let primary = global.get_primary_monitor();
Tweener.addTween(this._group, Tweener.addTween(this.workspaces.actor,
{ x: primary.x, { x: primary.x - this._group.x,
y: primary.y, y: primary.y - this._group.y,
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
transition: 'easeOutQuad', transition: 'easeOutQuad',
@ -490,9 +342,9 @@ Overview.prototype = {
onCompleteScope: this onCompleteScope: this
}); });
// Make Dash fade in so that it doesn't appear too big. // Make the other elements fade in.
this._dash.actor.opacity = 0; this._group.opacity = 0;
Tweener.addTween(this._dash.actor, Tweener.addTween(this._group,
{ opacity: 255, { opacity: 255,
transition: 'easeOutQuad', transition: 'easeOutQuad',
time: ANIMATION_TIME time: ANIMATION_TIME
@ -508,16 +360,24 @@ Overview.prototype = {
this.animationInProgress = true; this.animationInProgress = true;
this._hideInProgress = true; this._hideInProgress = true;
if (this._activeDisplayPane != null)
this._activeDisplayPane.close(); if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
}
this.workspaces.hide(); this.workspaces.hide();
// Create a zoom in effect by transforming the Overview group so that // Create a zoom in effect by transforming the workspaces view so that
// the active workspace fills up the whole screen. The opposite // the active workspace fills up the whole screen. The opposite
// transition is used in show(). // transition is used in show().
let scale = this.getZoomedInScale(); let scale = this.getZoomedInScale();
let [posX, posY] = this.getZoomedInPosition(); let [posX, posY] = this.getZoomedInPosition();
Tweener.addTween(this._group, Tweener.addTween(this.workspaces.actor,
{ x: posX, { x: posX,
y: posY, y: posY,
scaleX: scale, scaleX: scale,
@ -528,8 +388,8 @@ Overview.prototype = {
onCompleteScope: this onCompleteScope: this
}); });
// Make Dash fade out so that it doesn't appear to big. // Make other elements fade out.
Tweener.addTween(this._dash.actor, Tweener.addTween(this._group,
{ opacity: 0, { opacity: 0,
transition: 'easeOutQuad', transition: 'easeOutQuad',
time: ANIMATION_TIME time: ANIMATION_TIME
@ -565,6 +425,7 @@ Overview.prototype = {
return; return;
this.animationInProgress = false; this.animationInProgress = false;
this._desktopFade.hide();
this._coverPane.lower_bottom(); this._coverPane.lower_bottom();
this.emit('shown'); this.emit('shown');
@ -576,12 +437,12 @@ Overview.prototype = {
this.workspaces.destroy(); this.workspaces.destroy();
this.workspaces = null; this.workspaces = null;
this._workspacesBar.destroy(); this._workspacesDisplay.hide();
this._workspacesBar = null; this.viewSelector.hide();
this._workspacesManager = null;
this._dash.hide(); this._dash.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide(); this._group.hide();
this.visible = false; this.visible = false;
@ -590,7 +451,7 @@ Overview.prototype = {
this._coverPane.lower_bottom(); this._coverPane.lower_bottom();
Main.popModal(this._dash.actor); Main.popModal(this.viewSelector.actor);
this.emit('hidden'); this.emit('hidden');
} }
}; };

View File

@ -109,7 +109,7 @@ PlaceDeviceInfo.prototype = {
this._mount.unmount_finish(res); this._mount.unmount_finish(res);
} catch (e) { } catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name()); let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.infoBar.setMessage(message, Main.overview.shellInfo.setMessage(message,
Lang.bind(this, this.remove), Lang.bind(this, this.remove),
_("Retry")); _("Retry"));
} }

View File

@ -2,7 +2,7 @@
const Signals = imports.signals; const Signals = imports.signals;
const RESULT_ICON_SIZE = 24; const RESULT_ICON_SIZE = 48;
// Not currently referenced by the search API, but // Not currently referenced by the search API, but
// this enumeration can be useful for provider // this enumeration can be useful for provider
@ -182,7 +182,7 @@ SearchProvider.prototype = {
* implementation will show the icon next to the name. * implementation will show the icon next to the name.
* *
* The actor should be an instance of St.Widget, with the style class * The actor should be an instance of St.Widget, with the style class
* 'dash-search-result-content'. * 'search-result-content'.
*/ */
createResultActor: function(resultMeta, terms) { createResultActor: function(resultMeta, terms) {
return null; return null;

332
js/ui/searchDisplay.js Normal file
View File

@ -0,0 +1,332 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Gtk = imports.gi.Gtk;
const St = imports.gi.St;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 2;
function SearchResult(provider, metaInfo, terms) {
this._init(provider, metaInfo, terms);
}
SearchResult.prototype = {
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Clickable({ style_class: 'search-result',
reactive: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
let content = provider.createResultActor(metaInfo, terms);
if (content == null) {
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
track_hover: true });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: Lang.bind(this, function(size) {
return this.metaInfo['icon'];
})});
content.set_child(icon.actor);
}
this._content = content;
this.actor.set_child(content);
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
},
setSelected: function(selected) {
if (selected)
this._content.add_style_pseudo_class('selected');
else
this._content.remove_style_pseudo_class('selected');
},
activate: function() {
this.provider.activateResult(this.metaInfo.id);
Main.overview.toggle();
},
_onResultClicked: function(actor, event) {
this.activate();
},
getDragActorSource: function() {
return this.metaInfo['icon'];
},
getDragActor: function(stageX, stageY) {
return new Clutter.Clone({ source: this.metaInfo['icon'] });
},
shellWorkspaceLaunch: function() {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id);
else
this.provider.activateResult(this.metaInfo.id);
}
};
function GridSearchResults(provider) {
this._init(provider);
}
GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function(provider) {
Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
this.selectionIndex = -1;
},
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
},
renderResults: function(results, terms) {
for (let i = 0; i < results.length; i++) {
let result = results[i];
let meta = this.provider.getResultMeta(result);
let display = new SearchResult(this.provider, meta, terms);
this._grid.addItem(display.actor);
}
},
clear: function () {
this._grid.removeAll();
this.selectionIndex = -1;
},
selectIndex: function (index) {
let nVisible = this.getVisibleResultCount();
if (this.selectionIndex >= 0) {
let prevActor = this._grid.getItemAtIndex(this.selectionIndex);
prevActor._delegate.setSelected(false);
}
this.selectionIndex = -1;
if (index >= nVisible)
return false;
else if (index < 0)
return false;
let targetActor = this._grid.getItemAtIndex(index);
targetActor._delegate.setSelected(true);
this.selectionIndex = index;
return true;
},
activateSelected: function() {
if (this.selectionIndex < 0)
return;
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
targetActor._delegate.activate();
}
};
function SearchResults(searchSystem) {
this._init(searchSystem);
}
SearchResults.prototype = {
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this.actor = new St.Bin({ name: 'searchResults',
y_align: St.Align.START,
x_align: St.Align.START,
x_fill: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
let scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
vshadows: true });
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
this.actor.set_child(scrollView);
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._content.add(this._statusText);
this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++)
this.createProviderMeta(this._providers[i]);
},
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let titleButton = new St.Button({ style_class: 'search-section-header',
reactive: true,
x_fill: true,
y_fill: true });
titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
providerBox.add(titleButton);
let titleBox = new St.BoxLayout();
titleButton.set_child(titleBox);
let title = new St.Label({ text: provider.title });
let count = new St.Label();
titleBox.add(title, { expand: true });
titleBox.add(count);
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = provider.createResultContainerActor();
if (resultDisplay == null) {
resultDisplay = new GridSearchResults(provider);
}
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ actor: providerBox,
resultDisplay: resultDisplay,
count: count });
this._content.add(providerBox);
},
_clearDisplay: function() {
this._selectedProvider = -1;
this._visibleResultsCount = 0;
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
meta.actor.hide();
}
},
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
this._clearDisplay();
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching..."));
this._statusText.show();
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
updateSearch: function (searchString) {
let results = this._searchSystem.updateSearch(searchString);
this._clearDisplay();
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
return true;
} else {
this._statusText.hide();
}
let terms = this._searchSystem.getTerms();
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
meta.count.set_text('' + providerResults.length);
}
this.selectDown(false);
return true;
},
_onHeaderClicked: function(provider) {
provider.expandSearch(this._searchSystem.getTerms());
},
_modifyActorSelection: function(resultDisplay, up) {
let success;
let index = resultDisplay.getSelectionIndex();
if (up && index == -1)
index = resultDisplay.getVisibleResultCount() - 1;
else if (up)
index = index - 1;
else
index = index + 1;
return resultDisplay.selectIndex(index);
},
selectUp: function(recursing) {
for (let i = this._selectedProvider; i >= 0; i--) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, true);
if (success) {
this._selectedProvider = i;
return;
}
}
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = this._providerMeta.length - 1;
this.selectUp(true);
}
},
selectDown: function(recursing) {
let current = this._selectedProvider;
if (current == -1)
current = 0;
for (let i = current; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, false);
if (success) {
this._selectedProvider = i;
return;
}
}
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = 0;
this.selectDown(true);
}
},
activateSelected: function() {
let current = this._selectedProvider;
if (current < 0)
return;
let meta = this._providerMeta[current];
let resultDisplay = meta.resultDisplay;
resultDisplay.activateSelected();
Main.overview.hide();
}
};

View File

@ -469,11 +469,15 @@ Source.prototype = {
})); }));
} }
this._notification = undefined;
this._sentMessages = [];
// Since we only create sources when receiving a message, this // Since we only create sources when receiving a message, this
// is a plausible default // is a plausible default
this._presence = Telepathy.ConnectionPresenceType.AVAILABLE; this._presence = Telepathy.ConnectionPresenceType.AVAILABLE;
this._channelText = new Telepathy.ChannelText(DBus.session, connName, channelPath); this._channelText = new Telepathy.ChannelText(DBus.session, connName, channelPath);
this._sentId = this._channelText.connect('Sent', Lang.bind(this, this._messageSent));
this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived)); this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages)); this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
@ -517,6 +521,7 @@ Source.prototype = {
_channelClosed: function() { _channelClosed: function() {
this._channel.disconnect(this._closedId); this._channel.disconnect(this._closedId);
this._channelText.disconnect(this._receivedId); this._channelText.disconnect(this._receivedId);
this._channelText.disconnect(this._sentId);
this.destroy(); this.destroy();
}, },
@ -524,17 +529,30 @@ Source.prototype = {
if (!Main.messageTray.contains(this)) if (!Main.messageTray.contains(this))
Main.messageTray.add(this); Main.messageTray.add(this);
if (!this._notification) if (!this._notification) {
this._notification = new Notification(this); this._notification = new Notification(this);
for (let i = 0; i < this._sentMessages.length; i ++)
this._notification.appendMessage(this._sentMessages[i], false, true);
this._sentMessages = [];
}
}, },
_messageReceived: function(channel, id, timestamp, sender, _messageReceived: function(channel, id, timestamp, sender,
type, flags, text) { type, flags, text) {
this._ensureNotification(); this._ensureNotification();
this._notification.appendMessage(text); this._notification.appendMessage(text, false, false);
this.notify(this._notification); this.notify(this._notification);
}, },
_messageSent: function(channel, timestamp, type, text) {
if (this._notification) {
this._notification.appendMessage(text, false, true);
this.notify(this._notification);
} else {
this._sentMessages.push(text);
}
},
respond: function(text) { respond: function(text) {
this._channelText.SendRemote(Telepathy.ChannelTextMessageType.NORMAL, text); this._channelText.SendRemote(Telepathy.ChannelTextMessageType.NORMAL, text);
}, },
@ -565,7 +583,7 @@ Source.prototype = {
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>'; msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
this._ensureNotification(); this._ensureNotification();
this._notification.appendMessage(msg, true); this._notification.appendMessage(msg, true, false);
if (notify) if (notify)
this.notify(this._notification); this.notify(this._notification);
} }
@ -588,12 +606,12 @@ Notification.prototype = {
this._history = []; this._history = [];
}, },
appendMessage: function(text, asTitle) { appendMessage: function(text, asTitle, sent) {
if (asTitle) if (asTitle)
this.update(text, null, { customContent: true }); this.update(text, null, { customContent: true });
else else
this.update(this.source.title, text, { customContent: true }); this.update(this.source.title, text, { customContent: true });
this._append(text, 'chat-received'); this._append(text, sent ? 'chat-sent' : 'chat-received');
}, },
_append: function(text, style) { _append: function(text, style) {
@ -635,7 +653,6 @@ Notification.prototype = {
return; return;
this._responseEntry.set_text(''); this._responseEntry.set_text('');
this._append(text, 'chat-sent');
this.source.respond(text); this.source.respond(text);
} }
}; };

628
js/ui/viewSelector.js Normal file
View File

@ -0,0 +1,628 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Main = imports.ui.main;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const Tweener = imports.ui.tweener;
function SearchEntry() {
this._init();
}
SearchEntry.prototype = {
_init : function() {
this.actor = new St.Entry({ name: 'searchEntry',
hint_text: _("Search your computer") });
this.entry = this.actor.clutter_text;
this.actor.clutter_text.connect('text-changed', Lang.bind(this,
function() {
if (this.isActive())
this.actor.set_secondary_icon_from_file(global.imagedir +
'close-black.svg');
else
this.actor.set_secondary_icon_from_file(null);
}));
this.actor.connect('secondary-icon-clicked', Lang.bind(this,
function() {
this.reset();
}));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
this.pane = null;
this._capturedEventId = 0;
},
_updateCursorVisibility: function() {
let focus = global.stage.get_key_focus();
if (focus == global.stage || focus == this.entry)
this.entry.set_cursor_visible(true);
else
this.entry.set_cursor_visible(false);
},
show: function() {
if (this._capturedEventId == 0)
this._capturedEventId = global.stage.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
this.entry.set_cursor_visible(true);
this.entry.set_selection(0, 0);
},
hide: function() {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
},
reset: function () {
let [x, y, mask] = global.get_pointer();
let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE,
x, y);
// this.actor is never hovered directly, only its clutter_text and icon
let hovered = this.actor == actor.get_parent();
this.actor.set_hover(hovered);
this.entry.text = '';
// Return focus to the stage
global.stage.set_key_focus(null);
this.entry.set_cursor_visible(true);
this.entry.set_selection(0, 0);
},
getText: function () {
return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
},
// some search term has been entered
isActive: function() {
return this.actor.get_text() != '';
},
// the entry does not show the hint
_isActivated: function() {
return this.entry.text == this.actor.get_text();
},
_onCapturedEvent: function(actor, event) {
let source = event.get_source();
let panelEvent = source && Main.panel.actor.contains(source);
switch (event.type()) {
case Clutter.EventType.BUTTON_PRESS:
// the user clicked outside after activating the entry, but
// with no search term entered - cancel the search
if (source != this.entry && this.entry.text == '') {
this.reset();
// allow only panel events to continue
return !panelEvent;
}
return false;
case Clutter.EventType.KEY_PRESS:
// If neither the stage nor our entry have key focus, some
// "special" actor grabbed the focus (run dialog, looking
// glass); we don't want to interfere with that
let focus = global.stage.get_key_focus();
if (focus != global.stage && focus != this.entry)
return false;
let sym = event.get_key_symbol();
// If we have an active search, Escape cancels it - if we
// haven't, the key is ignored
if (sym == Clutter.Escape)
if (this._isActivated()) {
this.reset();
return true;
} else {
return false;
}
// Ignore non-printable keys
if (!Clutter.keysym_to_unicode(sym))
return false;
// Search started - move the key focus to the entry and
// "repeat" the event
if (!this._isActivated()) {
global.stage.set_key_focus(this.entry);
this.entry.event(event, false);
}
return false;
default:
// Suppress all other events outside the panel while the entry
// is activated and no search has been entered - any click
// outside the entry will cancel the search
return (this.entry.text == '' && !panelEvent);
}
},
_onDestroy: function() {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
}
};
Signals.addSignalMethods(SearchEntry.prototype);
function BaseTab(titleActor, pageActor) {
this._init(titleActor, pageActor);
}
BaseTab.prototype = {
_init: function(titleActor, pageActor) {
this.title = titleActor;
this.page = new St.Bin({ child: pageActor,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true,
style_class: 'view-tab-page' });
this.visible = false;
},
show: function() {
this.visible = true;
this.page.opacity = 0;
this.page.show();
Tweener.addTween(this.page,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad' });
},
hide: function() {
this.visible = false;
Tweener.addTween(this.page,
{ opacity: 0,
time: 0.1,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.page.hide();
})
});
},
_activate: function() {
this.emit('activated');
}
};
Signals.addSignalMethods(BaseTab.prototype);
function ViewTab(label, pageActor) {
this._init(label, pageActor);
}
ViewTab.prototype = {
__proto__: BaseTab.prototype,
_init: function(label, pageActor) {
let titleActor = new St.Button({ label: label,
style_class: 'view-tab-title' });
titleActor.connect('clicked', Lang.bind(this, this._activate));
BaseTab.prototype._init.call(this, titleActor, pageActor);
}
};
function SearchTab() {
this._init();
}
SearchTab.prototype = {
__proto__: BaseTab.prototype,
_init: function() {
this._searchActive = false;
this._searchPending = false;
this._keyPressId = 0;
this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
this._searchEntry = new SearchEntry();
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
BaseTab.prototype._init.call(this,
this._searchEntry.actor,
this._searchResults.actor);
this._searchEntry.entry.connect('text-changed',
Lang.bind(this, this._onTextChanged));
this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateSelected();
return true;
}));
},
setFindAsYouType: function(enabled) {
if (enabled)
this._searchEntry.show();
else
this._searchEntry.hide();
},
show: function() {
BaseTab.prototype.show.call(this);
if (this._keyPressId == 0)
this._keyPressId = global.stage.connect('key-press-event',
Lang.bind(this, this._onKeyPress));
},
hide: function() {
BaseTab.prototype.hide.call(this);
if (this._keyPressId > 0) {
global.stage.disconnect(this._keyPressId);
this._keyPressId = 0;
}
this._searchEntry.reset();
},
addSearchProvider: function(provider) {
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderMeta(provider);
},
_onTextChanged: function (se, prop) {
let searchPreviouslyActive = this._searchActive;
this._searchActive = this._searchEntry.isActive();
this._searchPending = this._searchActive && !searchPreviouslyActive;
if (this._searchPending) {
this._searchResults.startingSearch();
}
if (this._searchActive) {
this._activate();
} else {
this.emit('search-cancelled');
}
if (!this._searchActive) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
return;
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
},
_onKeyPress: function(stage, event) {
// If neither the stage nor the search entry have key focus, some
// "special" actor grabbed the focus (run dialog, looking glass);
// we don't want to interfere with that
let focus = stage.get_key_focus();
if (focus != stage && focus != this._searchEntry.entry)
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.Up) {
if (!this._searchActive)
return true;
this._searchResults.selectUp(false);
return true;
} else if (symbol == Clutter.Down) {
if (!this._searchActive)
return true;
this._searchResults.selectDown(false);
return true;
}
return false;
},
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._searchEntry.getText();
this._searchResults.updateSearch(text);
return false;
}
};
function ViewSelector() {
this._init();
}
ViewSelector.prototype = {
_init : function() {
this.actor = new St.BoxLayout({ name: 'viewSelector',
vertical: true });
// The tab bar is located at the top of the view selector and
// holds both "normal" tab labels and the search entry. The former
// is left aligned, the latter right aligned - unless the text
// direction is RTL, in which case the order is reversed.
this._tabBar = new Shell.GenericContainer();
this._tabBar.connect('get-preferred-width',
Lang.bind(this, this._getPreferredTabBarWidth));
this._tabBar.connect('get-preferred-height',
Lang.bind(this, this._getPreferredTabBarHeight));
this._tabBar.connect('allocate',
Lang.bind(this, this._allocateTabBar));
this.actor.add(this._tabBar);
// Box to hold "normal" tab labels
this._tabBox = new St.BoxLayout({ name: 'viewSelectorTabBar' });
this._tabBar.add_actor(this._tabBox);
// The searchArea just holds the entry
this._searchArea = new St.Bin({ name: 'searchArea' });
this._tabBar.add_actor(this._searchArea);
// The page area holds the tab pages. Every page is given the
// area's full allocation, so that the pages would appear on top
// of each other if the inactive ones weren't hidden.
this._pageArea = new Shell.Stack();
this.actor.add(this._pageArea, { x_fill: true,
y_fill: true,
expand: true });
this._tabs = [];
this._activeTab = null;
this._searchTab = new SearchTab();
this._searchArea.set_child(this._searchTab.title);
this._addTab(this._searchTab);
this._searchTab.connect('search-cancelled', Lang.bind(this,
function() {
this._switchTab(this._activeTab);
}));
this._keyPressId = 0;
this._itemDragBeginId = 0;
this._overviewHidingId = 0;
// Public constraints which may be used to tie actors' height or
// vertical position to the current tab's content; as the content's
// height and position depend on the view selector's style properties
// (e.g. font size, padding, spacing, ...) it would be extremely hard
// and ugly to get these from the outside. While it would be possible
// to use position and height properties directly, outside code would
// need to ensure that the content is properly allocated before
// accessing the properties.
this.constrainY = new Clutter.BindConstraint({ source: this._pageArea,
coordinate: Clutter.BindCoordinate.Y });
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
coordinate: Clutter.BindCoordinate.HEIGHT });
},
_addTab: function(tab) {
tab.page.hide();
this._pageArea.add_actor(tab.page);
tab.connect('activated', Lang.bind(this, function(tab) {
this._switchTab(tab);
}));
},
addViewTab: function(title, pageActor) {
let viewTab = new ViewTab(title, pageActor);
this._tabs.push(viewTab);
this._tabBox.add(viewTab.title);
this._addTab(viewTab);
},
_switchTab: function(tab) {
if (this._activeTab && this._activeTab.visible) {
if (this._activeTab == tab)
return;
this._activeTab.title.remove_style_pseudo_class('selected');
this._activeTab.hide();
}
if (tab != this._searchTab) {
tab.title.add_style_pseudo_class('selected');
this._activeTab = tab;
if (this._searchTab.visible) {
this._searchTab.hide();
}
}
if (!tab.visible)
tab.show();
// Pull a Meg Ryan:
if (Main.overview && Main.overview.workspaces) {
if (tab != this._tabs[0]) {
Tweener.addTween(Main.overview.workspaces.actor,
{ opacity: 0,
time: 0.1,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
Main.overview.workspaces.actor.hide();
Main.overview.workspaces.actor.opacity = 255;
})
});
} else {
Main.overview.workspaces.actor.opacity = 0;
Main.overview.workspaces.actor.show();
Tweener.addTween(Main.overview.workspaces.actor,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad' });
}
}
},
_switchDefaultTab: function() {
if (this._tabs.length > 0)
this._switchTab(this._tabs[0]);
},
_nextTab: function() {
if (this._tabs.length == 0 ||
this._tabs[this._tabs.length - 1] == this._activeTab)
return;
for (let i = 0; i < this._tabs.length; i++)
if (this._tabs[i] == this._activeTab) {
this._switchTab(this._tabs[i + 1]);
return;
}
},
_prevTab: function() {
if (this._tabs.length == 0 || this._tabs[0] == this._activeTab)
return;
for (let i = 0; i < this._tabs.length; i++)
if (this._tabs[i] == this._activeTab) {
this._switchTab(this._tabs[i - 1]);
return;
}
},
_getPreferredTabBarWidth: function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNat] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNat;
}
},
_getPreferredTabBarHeight: function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
},
_allocateTabBar: function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [searchMinWidth, searchNatWidth] = this._searchArea.get_preferred_width(-1);
let [barMinWidth, barNatWidth] = this._tabBox.get_preferred_width(-1);
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - barNatWidth;
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = barNatWidth;
}
this._tabBox.allocate(childBox, flags);
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = searchNatWidth;
} else {
childBox.x1 = allocWidth - searchNatWidth;
childBox.x2 = allocWidth;
}
this._searchArea.allocate(childBox, flags);
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
function() {
this.constrainY.offset = this.actor.y;
}));
},
_onKeyPress: function(stage, event) {
// Only process events if the stage has key focus - search is handled
// by the search tab, and we do not want to interfere with "special"
// actors grabbing focus (run dialog, looking glass, notifications).
let focus = stage.get_key_focus();
if (focus != stage)
return false;
let modifiers = Shell.get_event_state(event);
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
Main.overview.hide();
return true;
} else if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
if (symbol == Clutter.Page_Up) {
if (!this._searchActive)
this._prevTab();
return true;
} else if (symbol == Clutter.Page_Down) {
if (!this._searchActive)
this._nextTab();
return true;
}
}
return false;
},
addSearchProvider: function(provider) {
this._searchTab.addSearchProvider(provider);
},
show: function() {
this._searchTab.setFindAsYouType(true);
if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._switchDefaultTab));
if (this._overviewHidingId == 0)
this._overviewHidingId = Main.overview.connect('hiding',
Lang.bind(this, this._switchDefaultTab));
if (this._keyPressId == 0)
this._keyPressId = global.stage.connect('key-press-event',
Lang.bind(this, this._onKeyPress));
this._switchDefaultTab();
},
hide: function() {
this._searchTab.setFindAsYouType(false);
if (this._keyPressId > 0) {
global.stage.disconnect(this._keyPressId);
this._keyPressId = 0;
}
if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._itemDragBeginId);
this._itemDragBeginId = 0;
}
if (this._overviewHidingId > 0) {
Main.overview.disconnect(this._overviewHidingId);
this._overviewHidingId = 0;
}
}
};
Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -20,9 +20,6 @@ const FOCUS_ANIMATION_TIME = 0.15;
const WINDOW_DND_SIZE = 256; const WINDOW_DND_SIZE = 256;
const FRAME_COLOR = new Clutter.Color();
FRAME_COLOR.from_pixel(0xffffffff);
const SCROLL_SCALE_AMOUNT = 100 / 5; const SCROLL_SCALE_AMOUNT = 100 / 5;
const LIGHTBOX_FADE_TIME = 0.1; const LIGHTBOX_FADE_TIME = 0.1;
@ -54,11 +51,6 @@ function _clamp(value, min, max) {
return Math.max(min, Math.min(max, value)); return Math.max(min, Math.min(max, value));
} }
// Spacing between workspaces. At the moment, the same spacing is used
// in both zoomed-in and zoomed-out views; this is slightly
// metaphor-breaking, but the alternatives are also weird.
const GRID_SPACING = 15;
const FRAME_SIZE = GRID_SPACING / 3;
function ScaledPoint(x, y, scaleX, scaleY) { function ScaledPoint(x, y, scaleX, scaleY) {
[this.x, this.y, this.scaleX, this.scaleY] = arguments; [this.x, this.y, this.scaleX, this.scaleY] = arguments;
@ -289,76 +281,9 @@ WindowClone.prototype = {
this.emit('drag-end'); this.emit('drag-end');
} }
}; };
Signals.addSignalMethods(WindowClone.prototype); Signals.addSignalMethods(WindowClone.prototype);
function DesktopClone(window) {
this._init(window);
}
DesktopClone.prototype = {
_init : function(window) {
this.actor = new Clutter.Group({ reactive: true });
let background = new Clutter.Clone({ source: global.background_actor });
this.actor.add_actor(background);
if (window) {
this._desktop = new Clutter.Clone({ source: window.get_texture() });
this.actor.add_actor(this._desktop);
this._desktop.hide();
} else {
this._desktop = null;
}
this.actor.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
},
zoomFromOverview: function(fadeInIcons) {
if (this._desktop == null)
return;
if (fadeInIcons) {
this._desktop.opacity = 0;
this._desktop.show();
Tweener.addTween(this._desktop,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad' });
}
},
zoomToOverview: function(fadeOutIcons) {
if (this._desktop == null)
return;
if (fadeOutIcons) {
this._desktop.opacity = 255;
this._desktop.show();
Tweener.addTween(this._desktop,
{ opacity: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._desktop.hide();
})
});
} else {
this._desktop.hide();
}
},
_onButtonRelease : function (actor, event) {
this.emit('selected', event.get_time());
}
};
Signals.addSignalMethods(DesktopClone.prototype);
/** /**
* @windowClone: Corresponding window clone * @windowClone: Corresponding window clone
* @parentActor: The actor which will be the parent of all overlay items * @parentActor: The actor which will be the parent of all overlay items
@ -561,7 +486,6 @@ WindowOverlay.prototype = {
this._parentActor.queue_relayout(); this._parentActor.queue_relayout();
} }
}; };
Signals.addSignalMethods(WindowOverlay.prototype); Signals.addSignalMethods(WindowOverlay.prototype);
const WindowPositionFlags = { const WindowPositionFlags = {
@ -585,10 +509,20 @@ Workspace.prototype = {
// Without this the drop area will be overlapped. // Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0); this._windowOverlaysGroup.set_size(0, 0);
this.actor = new Clutter.Group(); this.actor = new Clutter.Group({ reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('button-release-event', Lang.bind(this,
function(actor, event) {
// Only switch to the workspace when there's no application
// windows open. The problem is that it's too easy to miss
// an app window and get the wrong one focused.
if (this._windows.length == 0) {
this.metaWorkspace.activate(event.get_time());
Main.overview.hide();
}
}));
// Items in _windowOverlaysGroup should not be scaled, so we don't // Items in _windowOverlaysGroup should not be scaled, so we don't
// add them to this.actor, but to its parent whenever it changes // add them to this.actor, but to its parent whenever it changes
@ -604,35 +538,10 @@ Workspace.prototype = {
let windows = global.get_window_actors().filter(this._isMyWindow, this); let windows = global.get_window_actors().filter(this._isMyWindow, this);
// Find the desktop window
for (let i = 0; i < windows.length; i++) {
if (windows[i].meta_window.get_window_type() == Meta.WindowType.DESKTOP) {
this._desktop = new DesktopClone(windows[i]);
break;
}
}
// If there wasn't one, fake it
if (!this._desktop)
this._desktop = new DesktopClone();
this._desktop.connect('selected',
Lang.bind(this,
function(clone, time) {
// Only switch to the workspace when there's no application windows
// open (we always have one window for the desktop). The problem
// is that it's too easy to miss an app window and get the wrong
// one focused.
if (this._windows.length == 1) {
this.metaWorkspace.activate(time);
Main.overview.hide();
}
}));
this.actor.add_actor(this._desktop.actor);
// Create clones for remaining windows that should be // Create clones for remaining windows that should be
// visible in the Overview // visible in the Overview
this._windows = [this._desktop]; this._windows = [];
this._windowOverlays = [ null ]; this._windowOverlays = [];
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) { if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i]); this._addWindowClone(windows[i]);
@ -651,8 +560,6 @@ Workspace.prototype = {
this._visible = false; this._visible = false;
this._frame = null;
this.leavingOverview = false; this.leavingOverview = false;
}, },
@ -714,9 +621,6 @@ Workspace.prototype = {
this._lightbox.show(); this._lightbox.show();
else else
this._lightbox.hide(); this._lightbox.hide();
if (this._frame)
this._frame.set_opacity(showLightbox ? 150 : 255);
}, },
/** /**
@ -737,32 +641,6 @@ Workspace.prototype = {
this._lightbox.highlight(actor); this._lightbox.highlight(actor);
}, },
// Mark the workspace selected/not-selected
setSelected : function(selected) {
// Don't draw a frame if we only have one workspace
if (selected && global.screen.n_workspaces > 1) {
if (this._frame)
return;
// FIXME: do something cooler-looking using clutter-cairo
this._frame = new Clutter.Rectangle({ color: FRAME_COLOR });
this.actor.add_actor(this._frame);
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
this._frame.lower_bottom();
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
} else {
if (!this._frame)
return;
this.actor.disconnect(this._framePosHandler);
this._frame.destroy();
this._frame = null;
}
},
/** /**
* setReactive: * setReactive:
* @reactive: %true iff the workspace should be reactive * @reactive: %true iff the workspace should be reactive
@ -770,14 +648,7 @@ Workspace.prototype = {
* Set the workspace (desktop) reactive * Set the workspace (desktop) reactive
**/ **/
setReactive: function(reactive) { setReactive: function(reactive) {
this._desktop.actor.reactive = reactive; this.actor.reactive = reactive;
},
_updateFramePosition : function() {
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
}, },
_isCloneVisible: function(clone) { _isCloneVisible: function(clone) {
@ -788,7 +659,7 @@ Workspace.prototype = {
* _getVisibleClones: * _getVisibleClones:
* *
* Returns a list WindowClone objects where the clone isn't filtered * Returns a list WindowClone objects where the clone isn't filtered
* out by any application filter. The clone for the desktop is excluded. * out by any application filter.
* The returned array will always be newly allocated; it is not in any * The returned array will always be newly allocated; it is not in any
* defined order, and thus it's convenient to call .sort() with your * defined order, and thus it's convenient to call .sort() with your
* choice of sorting function. * choice of sorting function.
@ -796,7 +667,7 @@ Workspace.prototype = {
_getVisibleClones: function() { _getVisibleClones: function() {
let visible = []; let visible = [];
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
if (!this._isCloneVisible(clone)) if (!this._isCloneVisible(clone))
@ -808,7 +679,7 @@ Workspace.prototype = {
}, },
_resetCloneVisibility: function () { _resetCloneVisibility: function () {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
@ -1007,9 +878,9 @@ Workspace.prototype = {
let buttonOuterHeight, captionHeight; let buttonOuterHeight, captionHeight;
let buttonOuterWidth = 0; let buttonOuterWidth = 0;
if (this._windowOverlays[1]) { if (this._windowOverlays[0]) {
[buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale;
} else } else
[buttonOuterHeight, captionHeight] = [0, 0]; [buttonOuterHeight, captionHeight] = [0, 0];
buttonOuterHeight /= this.scale; buttonOuterHeight /= this.scale;
@ -1157,8 +1028,8 @@ Workspace.prototype = {
// be after the workspace animation finishes. // be after the workspace animation finishes.
let [cloneX, cloneY] = clone.actor.get_position(); let [cloneX, cloneY] = clone.actor.get_position();
let [cloneWidth, cloneHeight] = clone.actor.get_size(); let [cloneWidth, cloneHeight] = clone.actor.get_size();
cloneX = this.gridX + this.scale * cloneX; cloneX = this.x + this.scale * cloneX;
cloneY = this.gridY + this.scale * cloneY; cloneY = this.y + this.scale * cloneY;
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth; cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight; cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
@ -1172,7 +1043,7 @@ Workspace.prototype = {
}, },
_fadeInAllOverlays: function() { _fadeInAllOverlays: function() {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
@ -1182,7 +1053,7 @@ Workspace.prototype = {
}, },
_hideAllOverlays: function() { _hideAllOverlays: function() {
for (let i = 1; i< this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
overlay.hide(); overlay.hide();
} }
@ -1197,8 +1068,8 @@ Workspace.prototype = {
let wsHeight = this.actor.height * this.scale; let wsHeight = this.actor.height * this.scale;
let pointerHasMoved = (this._cursorX != x && this._cursorY != y); let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
let inWorkspace = (this.gridX < x && x < this.gridX + wsWidth && let inWorkspace = (this.x < x && x < this.x + wsWidth &&
this.gridY < y && y < this.gridY + wsHeight); this.y < y && y < this.y + wsHeight);
if (pointerHasMoved && inWorkspace) { if (pointerHasMoved && inWorkspace) {
// store current cursor position // store current cursor position
@ -1309,8 +1180,8 @@ Workspace.prototype = {
}, },
// check for maximized windows on the workspace // check for maximized windows on the workspace
_haveMaximizedWindows: function() { hasMaximizedWindows: function() {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let metaWindow = this._windows[i].metaWindow; let metaWindow = this._windows[i].metaWindow;
if (metaWindow.showing_on_its_workspace() && if (metaWindow.showing_on_its_workspace() &&
metaWindow.maximized_horizontally && metaWindow.maximized_horizontally &&
@ -1322,7 +1193,7 @@ Workspace.prototype = {
// Animate the full-screen to Overview transition. // Animate the full-screen to Overview transition.
zoomToOverview : function() { zoomToOverview : function() {
this.actor.set_position(this.gridX, this.gridY); this.actor.set_position(this.x, this.y);
this.actor.set_scale(this.scale, this.scale); this.actor.set_scale(this.scale, this.scale);
// Position and scale the windows. // Position and scale the windows.
@ -1331,12 +1202,6 @@ Workspace.prototype = {
else else
this.positionWindows(WindowPositionFlags.ZOOM); this.positionWindows(WindowPositionFlags.ZOOM);
let active = global.screen.get_active_workspace();
let fadeInIcons = (Main.overview.animationInProgress &&
active == this.metaWorkspace &&
!this._haveMaximizedWindows());
this._desktop.zoomToOverview(fadeInIcons);
this._visible = true; this._visible = true;
}, },
@ -1354,7 +1219,7 @@ Workspace.prototype = {
this._doneLeavingOverview)); this._doneLeavingOverview));
// Position and scale the windows. // Position and scale the windows.
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
clone.zoomFromOverview(); clone.zoomFromOverview();
@ -1383,77 +1248,9 @@ Workspace.prototype = {
} }
} }
let active = global.screen.get_active_workspace();
let fadeOutIcons = (active == this.metaWorkspace &&
!this._haveMaximizedWindows());
this._desktop.zoomFromOverview(fadeOutIcons);
this._visible = false; this._visible = false;
}, },
// Animates grid shrinking/expanding when a row or column
// of workspaces is added or removed
resizeToGrid : function (oldScale) {
this._hideAllOverlays();
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, this._fadeInAllOverlays)
});
},
// Animates the addition of a new (empty) workspace
slideIn : function(oldScale) {
if (this.gridCol > this.gridRow) {
this.actor.set_position(global.screen_width, this.gridY);
this.actor.set_scale(oldScale, oldScale);
} else {
this.actor.set_position(this.gridX, global.screen_height);
this.actor.set_scale(this.scale, this.scale);
}
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._visible = true;
},
// Animates the removal of a workspace
slideOut : function(onComplete) {
let destX = this.actor.x, destY = this.actor.y;
this._hideAllOverlays();
if (this.gridCol > this.gridRow)
destX = global.screen_width;
else
destY = global.screen_height;
Tweener.addTween(this.actor,
{ x: destX,
y: destY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: onComplete
});
this._visible = false;
// Don't let the user try to select this workspace as it's
// making its exit.
this._desktop.reactive = false;
},
destroy : function() { destroy : function() {
this.actor.destroy(); this.actor.destroy();
}, },
@ -1475,7 +1272,7 @@ Workspace.prototype = {
// their parent (this.actor), but we might have a zoomed window // their parent (this.actor), but we might have a zoomed window
// which has been reparented to the stage - _windows[0] holds // which has been reparented to the stage - _windows[0] holds
// the desktop window, which is never reparented // the desktop window, which is never reparented
for (let w = 1; w < this._windows.length; w++) for (let w = 0; w < this._windows.length; w++)
this._windows[w].destroy(); this._windows[w].destroy();
this._windows = []; this._windows = [];
}, },
@ -1506,12 +1303,12 @@ Workspace.prototype = {
Lang.bind(this, this._onCloneSelected)); Lang.bind(this, this._onCloneSelected));
clone.connect('drag-begin', clone.connect('drag-begin',
Lang.bind(this, function(clone) { Lang.bind(this, function(clone) {
this.emit('window-drag-begin', clone.actor); Main.overview.beginWindowDrag();
overlay.hide(); overlay.hide();
})); }));
clone.connect('drag-end', clone.connect('drag-end',
Lang.bind(this, function(clone) { Lang.bind(this, function(clone) {
this.emit('window-drag-end', clone.actor); Main.overview.endWindowDrag();
overlay.show(); overlay.show();
})); }));
clone.connect('zoom-start', clone.connect('zoom-start',
@ -1534,7 +1331,7 @@ Workspace.prototype = {
}, },
_onShowOverlayClose: function (windowOverlay) { _onShowOverlayClose: function (windowOverlay) {
for (let i = 1; i < this._windowOverlays.length; i++) { for (let i = 0; i < this._windowOverlays.length; i++) {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (overlay == windowOverlay) if (overlay == windowOverlay)
continue; continue;
@ -1643,11 +1440,11 @@ function _workspaceRelativeModifier(workspace) {
} }
return [ { name: 'x', return [ { name: 'x',
parameters: { workspacePos: workspace.gridX, parameters: { workspacePos: workspace.x,
overviewPos: overviewPosX, overviewPos: overviewPosX,
overviewScale: overviewScale } }, overviewScale: overviewScale } },
{ name: 'y', { name: 'y',
parameters: { workspacePos: workspace.gridY, parameters: { workspacePos: workspace.y,
overviewPos: overviewPosY, overviewPos: overviewPosY,
overviewScale: overviewScale } } overviewScale: overviewScale } }
]; ];

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ js/ui/popupMenu.js
js/ui/runDialog.js js/ui/runDialog.js
js/ui/statusMenu.js js/ui/statusMenu.js
js/ui/status/accessibility.js js/ui/status/accessibility.js
js/ui/viewSelector.js
js/ui/windowAttentionHandler.js js/ui/windowAttentionHandler.js
js/ui/workspacesView.js js/ui/workspacesView.js
src/gvc/gvc-mixer-control.c src/gvc/gvc-mixer-control.c