Merge branch 'master' into datetime

This commit is contained in:
David Zeuthen 2011-01-24 15:27:13 -05:00
commit 50c5591ec2
54 changed files with 2383 additions and 862 deletions

View File

@ -101,11 +101,13 @@
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'videorate ! theoraenc ! oggmux' and records to Ogg Theora.
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>
</key>
<key name="file-extension" type="s">
<default>'ogv'</default>
<default>'webm'</default>
<_summary>File extension used for storing the screencast</_summary>
<_description>
The filename for recorded screencasts will be a unique filename

View File

@ -313,7 +313,7 @@ StTooltip StLabel {
background-image: url("close-window.svg");
height: 24px;
width: 24px;
-st-shadow: -2px 2px 6px rgba(0,0,0,0.5);
-st-background-image-shadow: -2px 2px 6px rgba(0,0,0,0.5);
-shell-close-overlap: 16px;
}
@ -374,7 +374,7 @@ StTooltip StLabel {
background-gradient-direction: vertical;
color: rgb(64, 64, 64);
font-weight: bold;
-st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
transition-duration: 0;
}
@ -391,6 +391,10 @@ StTooltip StLabel {
height: 24px;
}
.view-tab-title:hover {
color: #bbb;
}
.view-tab-title:selected {
color: #000000;
background-color: #c2c7cd;
@ -506,7 +510,7 @@ StTooltip StLabel {
.remove-favorite-icon:hover {
color: white;
-st-shadow: black 0px 2px 2px;
icon-shadow: black 0px 2px 2px;
}
.app-well-app > .overview-icon,
@ -1004,7 +1008,7 @@ StTooltip StLabel {
color: #545454;
background-color: #e8e8e8;
caret-color: #545454;
-st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
}
/* The spacing and padding on the summary is tricky; we want to keep

View File

@ -15,6 +15,8 @@ const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 750; // milliseconds
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
const THUMBNAIL_DEFAULT_SIZE = 256;
@ -50,6 +52,8 @@ AltTabPopup.prototype = {
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
this.thumbnailsVisible = false;
// Initially disable hover so we ignore the enter-event if
// the switcher appears underneath the current pointer location
this._disableHover();
@ -133,7 +137,7 @@ AltTabPopup.prototype = {
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this._appSwitcher = new AppSwitcher(apps);
this._appSwitcher = new AppSwitcher(apps, this);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
@ -457,11 +461,15 @@ AltTabPopup.prototype = {
},
_destroyThumbnails : function() {
Tweener.addTween(this._thumbnails.actor,
let thumbnailsActor = this._thumbnails.actor;
Tweener.addTween(thumbnailsActor,
{ opacity: 0,
time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad',
onComplete: function() { this.destroy(); }
onComplete: Lang.bind(this, function() {
thumbnailsActor.destroy();
this.thumbnailsVisible = false;
})
});
this._thumbnails = null;
},
@ -477,7 +485,8 @@ AltTabPopup.prototype = {
Tweener.addTween(this._thumbnails.actor,
{ opacity: 255,
time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad'
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
});
}
};
@ -591,16 +600,20 @@ SwitcherList.prototype = {
this._list.add_actor(bbox);
let n = this._items.length;
bbox.connect('clicked', Lang.bind(this, function () {
this._itemActivated(n);
}));
bbox.connect('enter-event', Lang.bind(this, function () {
this._itemEntered(n);
}));
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
this._items.push(bbox);
},
_onItemClicked: function (index) {
this._itemActivated(index);
},
_onItemEnter: function (index) {
this._itemEntered(index);
},
addSeparator: function () {
let box = new St.Bin({ style_class: 'separator' });
this._separator = box;
@ -819,14 +832,14 @@ AppIcon.prototype = {
}
};
function AppSwitcher(apps) {
this._init(apps);
function AppSwitcher(apps, altTabPopup) {
this._init(apps, altTabPopup);
}
AppSwitcher.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(apps) {
_init : function(apps, altTabPopup) {
SwitcherList.prototype._init.call(this, true);
// Construct the AppIcons, sort by time, add to the popup
@ -858,6 +871,8 @@ AppSwitcher.prototype = {
this._curApp = -1;
this._iconSize = 0;
this._altTabPopup = altTabPopup;
this._mouseTimeOutId = 0;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
@ -922,6 +937,27 @@ AppSwitcher.prototype = {
}
},
// We override SwitcherList's _onItemEnter method to delay
// activation when the thumbnail list is open
_onItemEnter: function (index) {
if (this._mouseTimeOutId != 0)
Mainloop.source_remove(this._mouseTimeOutId);
if (this._altTabPopup.thumbnailsVisible) {
this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT,
Lang.bind(this, function () {
this._enterItem(index);
}));
} else
this._itemEntered(index);
},
_enterItem: function(index) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
if (this._items[index].contains(pickedActor))
this._itemEntered(index);
},
// We override SwitcherList's highlight() method to also deal with
// the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
// will hide their arrows by default, but show them when their

View File

@ -40,9 +40,18 @@ AlphabeticalView.prototype = {
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
vshadows: true });
vfade: true });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
let adjustment = this.actor.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
}));
},
_removeAll: function() {

View File

@ -386,7 +386,7 @@ Notification.prototype = {
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
hscrollbar_policy: Gtk.PolicyType.NEVER,
vshadows: true });
vfade: true });
this.actor.add(this._scrollArea, { row: 1,
col: 1 });
this._contentArea = new St.BoxLayout({ name: 'notification-body',

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
@ -32,6 +33,18 @@ const DASH_SPLIT_FRACTION = 0.1;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const SwipeScrollDirection = {
NONE: 0,
HORIZONTAL: 1,
VERTICAL: 2
};
const SwipeScrollResult = {
CANCEL: 0,
SWIPE: 1,
CLICK: 2
};
function ShellInfo() {
this._init();
}
@ -98,7 +111,8 @@ Overview.prototype = {
this._spacing = 0;
this._group = new St.Group({ name: 'overview' });
this._group = new St.Group({ name: 'overview',
reactive: true });
this._group._delegate = this;
this._group.connect('style-changed',
Lang.bind(this, function() {
@ -110,6 +124,11 @@ Overview.prototype = {
}
}));
this._scrollDirection = SwipeScrollDirection.NONE;
this._scrollAdjustment = null;
this._capturedEventId = 0;
this._buttonPressId = 0;
this.shellInfo = new ShellInfo();
this._workspacesDisplay = null;
@ -154,7 +173,7 @@ Overview.prototype = {
this._dash.actor.add_constraint(this.viewSelector.constrainY);
this._dash.actor.add_constraint(this.viewSelector.constrainHeight);
this._coverPane.lower_bottom();
this._coverPane.hide();
// XDND
this._dragMonitor = {
@ -233,6 +252,149 @@ Overview.prototype = {
return DND.DragMotionResult.CONTINUE;
},
setScrollAdjustment: function(adjustment, direction) {
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
else
this._scrollDirection = direction;
},
_onButtonPress: function(actor, event) {
if (this._scrollDirection == SwipeScrollDirection.NONE)
return;
let [stageX, stageY] = event.get_coords();
this._dragStartX = this._dragX = stageX;
this._dragStartY = this._dragY = stageY;
this._dragStartValue = this._scrollAdjustment.value;
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
this._capturedEventId = global.stage.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
this.emit('swipe-scroll-begin');
},
_onCapturedEvent: function(actor, event) {
let stageX, stageY;
switch(event.type()) {
case Clutter.EventType.BUTTON_RELEASE:
[stageX, stageY] = event.get_coords();
// default to snapping back to the original value
let newValue = this._dragStartValue;
let minValue = this._scrollAdjustment.lower;
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
let direction;
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
direction = stageX > this._dragStartX ? -1 : 1;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
direction *= -1;
} else {
direction = stageY > this._dragStartY ? -1 : 1;
}
// We default to scroll a full page size; both the first
// and the last page may be smaller though, so we need to
// adjust difference in those cases.
let difference = direction * this._scrollAdjustment.page_size;
if (this._dragStartValue + difference > maxValue)
difference = maxValue - this._dragStartValue;
else if (this._dragStartValue + difference < minValue)
difference = minValue - this._dragStartValue;
// If the user has moved more than half the scroll
// difference, we want to "settle" to the new value
// even if the user stops dragging rather "throws" by
// releasing during the drag.
let distance = this._dragStartValue - this._scrollAdjustment.value;
let noStop = Math.abs(distance / difference) > 0.5;
// We detect if the user is stopped by comparing the
// timestamp of the button release with the timestamp of
// the last motion. Experimentally, a difference of 0 or 1
// millisecond indicates that the mouse is in motion, a
// larger difference indicates that the mouse is stopped.
if ((this._lastMotionTime > 0 &&
this._lastMotionTime > event.get_time() - 2) ||
noStop) {
if (this._dragStartValue + difference >= minValue &&
this._dragStartValue + difference <= maxValue)
newValue += difference;
}
// See if the user has moved the mouse enough to trigger
// a drag
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
if (Math.abs(stageX - this._dragStartX) < threshold &&
Math.abs(stageY - this._dragStartY) < threshold) {
// no motion? It's a click!
this.emit('swipe-scroll-end', SwipeScrollResult.CLICK);
} else {
let result;
if (newValue == this._dragStartValue)
result = SwipeScrollResult.CANCEL;
else
result = SwipeScrollResult.SWIPE;
// The event capture handler is disconnected
// while scrolling to the final position, so
// to avoid undesired prelights we raise
// the cover pane.
this._coverPane.raise_top();
this._coverPane.show();
Tweener.addTween(this._scrollAdjustment,
{ value: newValue,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onCompleteScope: this,
onComplete: function() {
this._coverPane.hide();
this.emit('swipe-scroll-end',
result);
}
});
}
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
return true;
case Clutter.EventType.MOTION:
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let dy = this._dragY - stageY;
let primary = global.get_primary_monitor();
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
else
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
} else {
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
}
this._dragX = stageX;
this._dragY = stageY;
this._lastMotionTime = event.get_time();
return true;
// Block enter/leave events to avoid prelights
// during swipe-scroll
case Clutter.EventType.ENTER:
case Clutter.EventType.LEAVE:
return true;
}
return false;
},
_getDesktopClone: function() {
let windows = global.get_window_actors().filter(function(w) {
return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP;
@ -335,6 +497,9 @@ Overview.prototype = {
this._modal = true;
this._animateVisible();
this._shown = true;
this._buttonPressId = this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
},
_animateVisible: function() {
@ -402,6 +567,7 @@ Overview.prototype = {
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
},
@ -432,6 +598,10 @@ Overview.prototype = {
this._shown = false;
this._syncInputMode();
if (this._buttonPressId > 0)
this._group.disconnect(this._buttonPressId);
this._buttonPressId = 0;
},
// hideTemporarily:
@ -541,13 +711,14 @@ Overview.prototype = {
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('hiding');
},
_showDone: function() {
this.animationInProgress = false;
this._desktopFade.hide();
this._coverPane.lower_bottom();
this._coverPane.hide();
this.emit('shown');
// Handle any calls to hide* while we were showing
@ -575,7 +746,7 @@ Overview.prototype = {
this.animationInProgress = false;
this._hideInProgress = false;
this._coverPane.lower_bottom();
this._coverPane.hide();
this.emit('hidden');
// Handle any calls to show* while we were hiding

View File

@ -44,6 +44,10 @@ const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
if (Config.HAVE_BLUETOOTH)
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
const STANDARD_TRAY_INDICATOR_FACTORIES = [
imports.ui.status.keyboard.ModifierIndicatorFactory
];
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
@ -695,17 +699,18 @@ Panel.prototype = {
/* right */
// System status applets live in statusBox, while legacy tray icons
// On-off indicators (for keyboard leds and accessx) are in indicatorBox
// System status applets live in statusBox, and legacy tray icons
// live in trayBox
// The trayBox is hidden when there are no tray icons.
let statusBox = new St.BoxLayout({ name: 'statusTray' });
let trayBox = new St.BoxLayout({ name: 'legacyTray' });
this._trayBox = trayBox;
this._statusBox = statusBox;
this._indicatorBox = new St.BoxLayout({ name: 'indicatorBox' });
this._trayBox = new St.BoxLayout({ name: 'legacyTray' });
this._statusBox = new St.BoxLayout({ name: 'statusTray' });
trayBox.hide();
this._rightBox.add(trayBox);
this._rightBox.add(statusBox);
this._trayBox.hide();
this._rightBox.add(this._indicatorBox);
this._rightBox.add(this._trayBox);
this._rightBox.add(this._statusBox);
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
@ -755,6 +760,13 @@ Panel.prototype = {
},
startStatusArea: function() {
for (let i = 0; i < STANDARD_TRAY_INDICATOR_FACTORIES.length; i++) {
let factory = new STANDARD_TRAY_INDICATOR_FACTORIES[i];
let indicators = factory.getIndicators();
for (let j = 0; j < indicators.length; j++)
this._indicatorBox.add(indicators[j]);
}
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
let role = STANDARD_TRAY_ICON_ORDER[i];
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];

View File

@ -10,6 +10,7 @@ const St = imports.gi.St;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 2;
@ -166,7 +167,7 @@ SearchResults.prototype = {
let scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
vshadows: true });
vfade: true });
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
@ -175,6 +176,15 @@ SearchResults.prototype = {
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
let adjustment = scrollView.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
}));
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._content.add(this._statusText);

View File

@ -42,7 +42,7 @@ LayoutMenuItem.prototype = {
};
function XKBIndicator() {
this._init.apply(this, arguments);
this._init.call(this);
}
XKBIndicator.prototype = {
@ -75,7 +75,7 @@ XKBIndicator.prototype = {
this._sync_config();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Keyboard Settings"), function() {
this.menu.addAction(_("Localization Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center region');
});
},
@ -203,4 +203,53 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
};
};
function ModifierIndicatorFactory() {
this._init.call(this);
}
ModifierIndicatorFactory.prototype = {
_init: function() {
this._settings = new Gio.Settings({ schema: INDICATOR_SCHEMA });
this._settings.connect('changed::show-keyboard-leds-indicator', Lang.bind(this, this._changed));
this._config = Gkbd.Configuration.get();
this._config.connect('indicators-changed', Lang.bind(this, this._changed));
this._scrollLock = new St.Icon({ icon_name: 'kbdled-scroll-lock', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._numLock = new St.Icon({ icon_name: 'kbdled-num-lock', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._capsLock = new St.Icon({ icon_name: 'kbdled-caps-lock', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._changed();
},
getIndicators: function() {
return [this._scrollLock, this._numLock, this._capsLock];
},
_changed: function() {
let enable = this._settings.get_boolean('show-keyboard-leds-indicator');
if (enable) {
if (this._config.get_scroll_lock_state())
this._scrollLock.show();
else
this._scrollLock.hide();
if (this._config.get_num_lock_state())
this._numLock.show();
else
this._numLock.hide();
if (this._config.get_caps_lock_state())
this._capsLock.show();
else
this._capsLock.hide();
} else {
this._scrollLock.hide();
this._numLock.hide();
this._capsLock.hide();
}
}
};

View File

@ -517,20 +517,10 @@ Workspace.prototype = {
// Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0);
this.actor = new Clutter.Group({ reactive: true });
this.actor = new Clutter.Group();
this.actor._delegate = this;
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
// add them to this.actor, but to its parent whenever it changes
@ -605,6 +595,10 @@ Workspace.prototype = {
return this._lookupIndex(metaWindow) >= 0;
},
isEmpty: function() {
return this._windows.length == 0;
},
setShowOnlyWindows: function(showOnlyWindows, reposition) {
this._showOnlyWindows = showOnlyWindows;
this._resetCloneVisibility();

View File

@ -45,6 +45,8 @@ WorkspacesView.prototype = {
this._spacing = node.get_length('spacing');
this._computeWorkspacePositions();
}));
this.actor.connect('notify::mapped',
Lang.bind(this, this._onMappedChanged));
this._width = width;
this._height = height;
@ -55,10 +57,9 @@ WorkspacesView.prototype = {
this._activeWorkspaceY = 0; // y offset of active ws while dragging
this._lostWorkspaces = [];
this._animating = false; // tweening
this._scrolling = false; // dragging desktop
this._scrolling = false; // swipe-scrolling
this._animatingScroll = false; // programatically updating the adjustment
this._inDrag = false; // dragging a window
this._lastMotionTime = -1; // used to track "stopping" while dragging workspaces
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
this._workspaces = workspaces;
@ -89,10 +90,6 @@ WorkspacesView.prototype = {
this._scrollAdjustment.connect('notify::value',
Lang.bind(this, this._onScroll));
this._dragIndex = -1;
this._buttonPressId = 0;
this._capturedEventId = 0;
this._timeoutId = 0;
this._windowSelectionAppId = null;
@ -113,6 +110,8 @@ WorkspacesView.prototype = {
Lang.bind(this, this._dragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._dragEnd));
this._swipeScrollBeginId = 0;
this._swipeScrollEndId = 0;
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
@ -290,8 +289,6 @@ WorkspacesView.prototype = {
if (this._inDrag)
scale *= WORKSPACE_DRAGGING_SCALE;
this._setWorkspaceDraggable(active, true);
let _width = this._workspaces[0].actor.width * scale;
let _height = this._workspaces[0].actor.height * scale;
@ -323,128 +320,6 @@ WorkspacesView.prototype = {
this._updateScrollAdjustment(active, showAnimation);
},
// _setWorkspaceDraggable:
// @index: workspace index
// @draggable: whether workspace @index should be draggable
//
// If @draggable is %true, set up workspace @index to allow switching
// workspaces by dragging the desktop - if a draggable workspace has
// been set up before, it will be reset before the new one is made
// draggable.
// If @draggable is %false, workspace @index is reset to no longer allow
// dragging.
_setWorkspaceDraggable: function(index, draggable) {
if (index < 0 || index >= global.n_workspaces)
return;
let dragActor = this._workspaces[index].actor;
if (draggable) {
this._workspaces[index].actor.reactive = true;
// reset old draggable workspace
if (this._dragIndex > -1)
this._setWorkspaceDraggable(this._dragIndex, false);
this._dragIndex = index;
this._buttonPressId = dragActor.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
} else {
this._dragIndex = -1;
if (this._buttonPressId > 0) {
if (dragActor.get_stage())
dragActor.disconnect(this._buttonPressId);
this._buttonPressId = 0;
}
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
}
},
// start dragging the active workspace
_onButtonPress: function(actor, event) {
if (actor != event.get_source())
return;
if (this._dragIndex == -1)
return;
let [stageX, stageY] = event.get_coords();
this._dragStartX = this._dragX = stageX;
this._scrolling = true;
this._capturedEventId = global.stage.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
},
// handle captured events while dragging a workspace
_onCapturedEvent: function(actor, event) {
let active = global.screen.get_active_workspace_index();
let stageX, stageY;
switch (event.type()) {
case Clutter.EventType.BUTTON_RELEASE:
this._scrolling = false;
[stageX, stageY] = event.get_coords();
// default to snapping back to the original workspace
let activate = this._dragIndex;
let last = global.screen.n_workspaces - 1;
// If the user has moved more than half a workspace, we want to "settle"
// to the new workspace even if the user stops dragging rather "throws"
// by releasing during the drag.
let noStop = Math.abs(activate - this._scrollAdjustment.value) > 0.5;
let difference = stageX > this._dragStartX ? -1 : 1;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
difference *= -1;
// We detect if the user is stopped by comparing the timestamp of the button
// release with the timestamp of the last motion. Experimentally, a difference
// of 0 or 1 millisecond indicates that the mouse is in motion, a larger
// difference indicates that the mouse is stopped.
if ((this._lastMotionTime > 0 && this._lastMotionTime > event.get_time() - 2) || noStop) {
if (activate + difference >= 0 &&
activate + difference <= last)
activate += difference;
}
if (activate != active) {
let workspace = this._workspaces[activate].metaWorkspace;
workspace.activate(global.get_current_time());
} else {
this._scrollToActive(true);
}
if (stageX == this._dragStartX)
// no motion? It's a click!
return false;
return true;
case Clutter.EventType.MOTION:
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let primary = global.get_primary_monitor();
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
this._scrollAdjustment.value -= (dx / primary.width);
else
this._scrollAdjustment.value += (dx / primary.width);
this._dragX = stageX;
this._lastMotionTime = event.get_time();
return true;
}
return false;
},
// Update workspace actors parameters to the values calculated in
// _computeWorkspacePositions()
// @showAnimation: iff %true, transition between states
@ -454,7 +329,6 @@ WorkspacesView.prototype = {
let targetWorkspaceCurrentX = this._workspaces[active].x;
let dx = targetWorkspaceNewX - targetWorkspaceCurrentX;
this._setWorkspaceDraggable(active, true);
this._animating = showAnimation;
for (let w = 0; w < this._workspaces.length; w++) {
@ -606,7 +480,6 @@ WorkspacesView.prototype = {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
global.screen.disconnect(this._restackedNotifyId);
this._setWorkspaceDraggable(this._dragIndex, false);
if (this._timeoutId) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
@ -629,6 +502,21 @@ WorkspacesView.prototype = {
}
},
_onMappedChanged: function() {
if (this.actor.mapped) {
let direction = Overview.SwipeScrollDirection.HORIZONTAL;
Main.overview.setScrollAdjustment(this._scrollAdjustment,
direction);
this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
Lang.bind(this, this._swipeScrollBegin));
this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end',
Lang.bind(this, this._swipeScrollEnd));
} else {
Main.overview.disconnect(this._swipeScrollBeginId);
Main.overview.disconnect(this._swipeScrollEndId);
}
},
_dragBegin: function() {
if (this._scrolling)
return;
@ -736,6 +624,37 @@ WorkspacesView.prototype = {
this._workspaces[i].setReservedSlot(null);
},
_swipeScrollBegin: function() {
this._scrolling = true;
},
_swipeScrollEnd: function(overview, result) {
this._scrolling = false;
if (result == Overview.SwipeScrollResult.CLICK) {
let [x, y, mod] = global.get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
x, y);
// 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.
let active = global.screen.get_active_workspace_index();
if (this._workspaces[active].isEmpty() &&
this.actor.contains(actor))
Main.overview.hide();
}
if (result == Overview.SwipeScrollResult.SWIPE)
// The active workspace has changed; while swipe-scrolling
// has already taken care of the positioning, the cached
// positions need to be updated
this._computeWorkspacePositions();
// Make sure title captions etc are shown as necessary
this._updateVisibility();
},
// sync the workspaces' positions to the value of the scroll adjustment
// and change the active workspace if appropriate
_onScroll: function(adj) {

View File

@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-19 22:47+0200\n"
"PO-Revision-Date: 2011-01-19 22:47+0300\n"
"POT-Creation-Date: 2011-01-23 13:29+0200\n"
"PO-Revision-Date: 2011-01-23 13:29+0300\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n"
"MIME-Version: 1.0\n"
@ -70,7 +70,8 @@ msgstr ""
msgid "List of desktop file IDs for favorite applications"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
"used for gst-launch. The pipeline should have an unconnected sink pad where "
@ -79,45 +80,47 @@ msgid ""
"pipeline can also take care of its own output - this might be used to send "
"the output to an icecast server via shout2send or similar. When unset or set "
"to an empty value, the default pipeline will be used. This is currently "
"'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:12
msgid "Show date in clock"
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
"at the optimal thread count on the system."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:13
msgid "Show the week date in the calendar"
msgid "Show date in clock"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show time with seconds"
msgid "Show the week date in the calendar"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show time with seconds"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "The gstreamer pipeline used to encode the screencast"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -125,15 +128,15 @@ msgid ""
"remove already saved data."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "Uuids of extensions to disable"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Whether to collect stats about applications usage"
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "disabled OpenSearch providers"
msgstr ""
@ -436,54 +439,54 @@ msgid "Applications"
msgstr "التطبيقات"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:479
#: ../js/ui/panel.js:483
#, c-format
msgid "Quit %s"
msgstr "أغلق %s"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/panel.js:564
#: ../js/ui/panel.js:568
msgid "%a %b %e, %R:%S"
msgstr "%A %e %B، %R:%S"
#: ../js/ui/panel.js:565
#: ../js/ui/panel.js:569
msgid "%a %b %e, %R"
msgstr "%A %e %B، %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/panel.js:569
#: ../js/ui/panel.js:573
msgid "%a %R:%S"
msgstr "%A %R:%S"
#: ../js/ui/panel.js:570
#: ../js/ui/panel.js:574
msgid "%a %R"
msgstr "%A %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/panel.js:577
#: ../js/ui/panel.js:581
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%A %e %B، %l:%M:%S %p"
#: ../js/ui/panel.js:578
#: ../js/ui/panel.js:582
msgid "%a %b %e, %l:%M %p"
msgstr "%A %e %B، %l:%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/panel.js:582
#: ../js/ui/panel.js:586
msgid "%a %l:%M:%S %p"
msgstr "%A %l:%M:%S %p"
#: ../js/ui/panel.js:583
#: ../js/ui/panel.js:587
msgid "%a %l:%M %p"
msgstr "%A %Ol:%OM %p"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:728
#: ../js/ui/panel.js:732
msgid "Activities"
msgstr "الأنشطة"
@ -638,7 +641,7 @@ msgstr "عطل أثناء تصفّح الجهاز"
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "تعذّر تصفح الجهاز، رسالة العطل '%s'"
#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/keyboard.js:78
#: ../js/ui/status/bluetooth.js:251
msgid "Keyboard Settings"
msgstr "إعدادات لوحة المفاتيح"
@ -713,6 +716,10 @@ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
msgid "OK"
msgstr "حسنا"
#: ../js/ui/status/keyboard.js:78
msgid "Localization Settings"
msgstr "إعدادات اللغة"
#: ../js/ui/status/power.js:85
msgid "Power Settings"
msgstr "إعدادات الطاقة"

120
po/es.po
View File

@ -1,17 +1,17 @@
# Spanish translation of gnome-shell.
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2010, 2011.
# Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell.master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2011-01-17 21:45+0000\n"
"PO-Revision-Date: 2011-01-19 16:17+0100\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"POT-Creation-Date: 2011-01-21 18:46+0000\n"
"PO-Revision-Date: 2011-01-22 16:34+0100\n"
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -76,7 +76,18 @@ msgstr "Si es cierta muestra la fecha de semana ISO en el calendario."
msgid "List of desktop file IDs for favorite applications"
msgstr "Lista de ID de archivos de escritorio para las aplicaciones favoritas"
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#, no-c-format
#| msgid ""
#| "Sets the GStreamer pipeline used to encode recordings. It follows the "
#| "syntax used for gst-launch. The pipeline should have an unconnected sink "
#| "pad where the recorded video is recorded. It will normally have a "
#| "unconnected source pad; output from that pad will be written into the "
#| "output file. However the pipeline can also take care of its own output - "
#| "this might be used to send the output to an icecast server via shout2send "
#| "or similar. When unset or set to an empty value, the default pipeline "
#| "will be used. This is currently 'videorate ! theoraenc ! oggmux' and "
#| "records to Ogg Theora."
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
"used for gst-launch. The pipeline should have an unconnected sink pad where "
@ -85,32 +96,35 @@ msgid ""
"pipeline can also take care of its own output - this might be used to send "
"the output to an icecast server via shout2send or similar. When unset or set "
"to an empty value, the default pipeline will be used. This is currently "
"'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
"at the optimal thread count on the system."
msgstr ""
"Establece la tubería GStreamer usada para codificar grabaciones. Sigue la "
"sintaxis usada para gst-launch. La tubería debería tener un sumidero («sink») "
"de ensamblaje/sesensamblaje donde el vídeo que se está grabando se graba. "
"Generalmente tendrá un origen de ensamblado/desensamblado; la salida de ese "
"punto se escibirá en el archivo de salida. No obstante la tubería también "
"puede tomar parte en su propia salida; esto se puede usar para enviar la "
"salida a un servidor «icecast» a través de shout2send o similar. Cuando no "
"está establecido o lo está a un valor vacío, se usará la tubería "
"predeterminada. Actualmente es «videorate ! theoraenc ! oggmux» y greba en "
"Ogg Theora."
"sintaxis usada para gst-launch. La tubería debería tener un sumidero "
"(«sink») de ensamblaje/sesensamblaje donde el vídeo que se está grabando se "
"graba. Generalmente tendrá un origen de ensamblado/desensamblado; la salida "
"de ese punto se escibirá en el archivo de salida. No obstante la tubería "
"también puede tomar parte en su propia salida; esto se puede usar para "
"enviar la salida a un servidor «icecast» a través de shout2send o similar. "
"Cuando no está establecido o lo está a un valor vacío, se usará la tubería "
"predeterminada. Actualmente es «videorate ! vp8enc quality=10 speed=2 "
"threads=%T ! queue ! webmmux» y greba en WEBM usando el códec VP8. Se usa %T "
"como suposición para el número de hilos óptimos en el sistema."
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.h:13
msgid "Show date in clock"
msgstr "Mostrar la fecha en el reloj"
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show the week date in the calendar"
msgstr "Mostrar la fecha de la semana en el calendario"
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show time with seconds"
msgstr "Mostrar la hora con segundos"
#: ../data/org.gnome.shell.gschema.xml.in.h:15
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
@ -118,7 +132,7 @@ msgstr ""
"Las aplicaciones correspondientes con esos identificadores se mostrarán en "
"el área de favoritos."
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -128,7 +142,7 @@ msgstr ""
"basado en la fecha actual y usará esta extensión. Se debería cambiar al "
"grabar en otro formato contenedor diferente."
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -136,11 +150,11 @@ msgstr ""
"La tasa de fotogramas de la grabación resultante grabada por el grabador de "
"«screencast» de GNOME Shell, en fotogramas por segundo."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "La tubería de gstreamer usada para codificar el «screencast»"
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -152,16 +166,16 @@ msgstr ""
"mantienen de forma privada, puede querer desactivarlo por razones de "
"privacidad. Note que haciéndolo no eliminará los datos ya guardados."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "Uuids of extensions to disable"
msgstr "Uuid de las extensiones que desactivar"
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Whether to collect stats about applications usage"
msgstr ""
"Indica si se deben recolectar estadísticas acerca del uso de las aplicaciones"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "disabled OpenSearch providers"
msgstr "proveedores OpenSearch desactivados"
@ -482,7 +496,7 @@ msgstr "Ver fuente"
msgid "Web Page"
msgstr "Página web"
#: ../js/ui/messageTray.js:1748
#: ../js/ui/messageTray.js:1765
msgid "System Information"
msgstr "Información del sistema"
@ -499,54 +513,54 @@ msgid "Applications"
msgstr "Aplicaciones"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:479
#: ../js/ui/panel.js:483
#, c-format
msgid "Quit %s"
msgstr "Salir de %s"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/panel.js:564
#: ../js/ui/panel.js:568
msgid "%a %b %e, %R:%S"
msgstr "%a %e de %b, %R:%S"
#: ../js/ui/panel.js:565
#: ../js/ui/panel.js:569
msgid "%a %b %e, %R"
msgstr "%a %e de %b, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/panel.js:569
#: ../js/ui/panel.js:573
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/panel.js:570
#: ../js/ui/panel.js:574
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/panel.js:577
#: ../js/ui/panel.js:581
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %e de %b, %H:%M:%S"
#: ../js/ui/panel.js:578
#: ../js/ui/panel.js:582
msgid "%a %b %e, %l:%M %p"
msgstr "%a %e de %b, %H:%M"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/panel.js:582
#: ../js/ui/panel.js:586
msgid "%a %l:%M:%S %p"
msgstr "%a %H:%M:%S"
#: ../js/ui/panel.js:583
#: ../js/ui/panel.js:587
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:728
#: ../js/ui/panel.js:732
msgid "Activities"
msgstr "Actividades"
@ -701,7 +715,7 @@ msgstr "Error al examinar el dispositivo"
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "No se puede examinar el dispositivo solicitado, el error es «%s»"
#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/keyboard.js:78
#: ../js/ui/status/bluetooth.js:251
msgid "Keyboard Settings"
msgstr "Configuración del teclado"
@ -776,6 +790,11 @@ msgstr "Introduzca el PIN mencionado en el dispositivo."
msgid "OK"
msgstr "Aceptar"
#: ../js/ui/status/keyboard.js:78
#| msgid "Sound Settings"
msgid "Localization Settings"
msgstr "Configuración regional"
#: ../js/ui/status/power.js:85
msgid "Power Settings"
msgstr "Configuración de energía"
@ -1015,8 +1034,8 @@ msgstr "%1$s: %2$s"
#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
#~ "in time."
#~ msgstr ""
#~ "Si es cierta y el formato es «12-horas» o «24-horas», muestra los segundos "
#~ "en la hora."
#~ "Si es cierta y el formato es «12-horas» o «24-horas», muestra los "
#~ "segundos en la hora."
#~ msgid ""
#~ "This key specifies the format used by the panel clock when the format key "
@ -1033,18 +1052,19 @@ msgstr "%1$s: %2$s"
#~ msgid ""
#~ "This key specifies the hour format used by the panel clock. Possible "
#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. 1970-"
#~ "01-01. If set to \"custom\", the clock will display time according to the "
#~ "format specified in the custom_format key. Note that if set to either "
#~ "\"unix\" or \"custom\", the show_date and show_seconds keys are ignored."
#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
#~ "1970-01-01. If set to \"custom\", the clock will display time according "
#~ "to the format specified in the custom_format key. Note that if set to "
#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
#~ "ignored."
#~ msgstr ""
#~ "Esta clave especifica el formato de la hora especificado por el reloj del "
#~ "panel. Los valores posibles son «12-hour» (12 horas), «24-hour» (24 horas), "
#~ "«unix» y «custom» (personalizado).Si se establece a «unix» el reloj mostrará "
#~ "la hora en segundos desde la época (1 de enero de 1970). Si se establece "
#~ "a «custom» el reloj mostrará la hora según el formato especificado en la "
#~ "clave «custom_format». Note que si se establece a «unix» o «custom» se "
#~ "ignoran las claves «show_date» y «show_seconds»."
#~ "panel. Los valores posibles son «12-hour» (12 horas), «24-hour» (24 "
#~ "horas), «unix» y «custom» (personalizado).Si se establece a «unix» el "
#~ "reloj mostrará la hora en segundos desde la época (1 de enero de 1970). "
#~ "Si se establece a «custom» el reloj mostrará la hora según el formato "
#~ "especificado en la clave «custom_format». Note que si se establece a "
#~ "«unix» o «custom» se ignoran las claves «show_date» y «show_seconds»."
#~ msgid "Clock Format"
#~ msgstr "Formato del reloj"

View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-18 13:02+0200\n"
"PO-Revision-Date: 2011-01-18 13:05+0200\n"
"POT-Creation-Date: 2011-01-23 02:42+0200\n"
"PO-Revision-Date: 2011-01-23 02:43+0200\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
"Language-Team: Hebrew <he@li.org>\n"
"MIME-Version: 1.0\n"
@ -73,7 +73,8 @@ msgstr "If true, display the ISO week date in the calendar."
msgid "List of desktop file IDs for favorite applications"
msgstr "List of desktop file IDs for favorite applications"
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
"used for gst-launch. The pipeline should have an unconnected sink pad where "
@ -82,7 +83,9 @@ msgid ""
"pipeline can also take care of its own output - this might be used to send "
"the output to an icecast server via shout2send or similar. When unset or set "
"to an empty value, the default pipeline will be used. This is currently "
"'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
"at the optimal thread count on the system."
msgstr ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
"used for gst-launch. The pipeline should have an unconnected sink pad where "
@ -91,51 +94,53 @@ msgstr ""
"pipeline can also take care of its own output - this might be used to send "
"the output to an icecast server via shout2send or similar. When unset or set "
"to an empty value, the default pipeline will be used. This is currently "
"'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
"at the optimal thread count on the system."
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.h:13
msgid "Show date in clock"
msgstr "Show date in clock"
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show the week date in the calendar"
msgstr "Show the week date in the calendar"
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show time with seconds"
msgstr "Show time with seconds"
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
msgstr ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
msgstr ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
msgstr ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
msgstr ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "The gstreamer pipeline used to encode the screencast"
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -147,15 +152,15 @@ msgstr ""
"want to disable this for privacy reasons. Please note that doing so won't "
"remove already saved data."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "Uuids of extensions to disable"
msgstr "Uuids of extensions to disable"
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Whether to collect stats about applications usage"
msgstr "Whether to collect stats about applications usage"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "disabled OpenSearch providers"
msgstr "disabled OpenSearch providers"
@ -471,7 +476,7 @@ msgstr "צפייה במקור"
msgid "Web Page"
msgstr "דף אינטרנט"
#: ../js/ui/messageTray.js:1748
#: ../js/ui/messageTray.js:1765
msgid "System Information"
msgstr "פרטי המערכת"
@ -488,54 +493,54 @@ msgid "Applications"
msgstr "יישומים"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:479
#: ../js/ui/panel.js:483
#, c-format
msgid "Quit %s"
msgstr "יציאה מ־%s"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/panel.js:564
#: ../js/ui/panel.js:568
msgid "%a %b %e, %R:%S"
msgstr "%a %b %e, %R:%S"
#: ../js/ui/panel.js:565
#: ../js/ui/panel.js:569
msgid "%a %b %e, %R"
msgstr "%a %b %e, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/panel.js:569
#: ../js/ui/panel.js:573
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/panel.js:570
#: ../js/ui/panel.js:574
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/panel.js:577
#: ../js/ui/panel.js:581
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %b %e, %l:%M:%S %p"
#: ../js/ui/panel.js:578
#: ../js/ui/panel.js:582
msgid "%a %b %e, %l:%M %p"
msgstr "%a %b %e, %l:%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/panel.js:582
#: ../js/ui/panel.js:586
msgid "%a %l:%M:%S %p"
msgstr "%a %l:%M:%S %p"
#: ../js/ui/panel.js:583
#: ../js/ui/panel.js:587
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:728
#: ../js/ui/panel.js:732
msgid "Activities"
msgstr "פעילויות"
@ -690,7 +695,7 @@ msgstr "שגיאה בעיון בהתקן"
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "לא ניתן לעיין בהתקן הנבחר, השגיאה היא '%s'"
#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/keyboard.js:78
#: ../js/ui/status/bluetooth.js:251
msgid "Keyboard Settings"
msgstr "הגדרות מקלדת"
@ -765,6 +770,10 @@ msgstr "נא להזין את קוד ה־PIN המוזכר בהתקן."
msgid "OK"
msgstr "אישור"
#: ../js/ui/status/keyboard.js:78
msgid "Localization Settings"
msgstr "הגדרות אוזריות"
#: ../js/ui/status/power.js:85
msgid "Power Settings"
msgstr "הגדרות צריכת החשמל"

102
po/it.po
View File

@ -8,14 +8,14 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-15 01:10+0100\n"
"POT-Creation-Date: 2011-01-21 01:16+0100\n"
"PO-Revision-Date: 2011-01-15 01:19+0100\n"
"Last-Translator: Luca Ferretti <lferrett@gnome.org>\n"
"Language-Team: Italian <tp@lists.linux.it>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
@ -161,6 +161,10 @@ msgid "Whether to collect stats about applications usage"
msgstr ""
"Indica se raccogliere statistiche riguardo l'utilizzo delle applicazioni"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "disabled OpenSearch providers"
msgstr ""
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:1
msgid "Clip the crosshairs at the center"
msgstr ""
@ -296,7 +300,7 @@ msgstr "Impossibile analizzare il comando:"
msgid "No such application"
msgstr "Applicazione inesistente"
#: ../js/misc/util.js:143 ../js/ui/runDialog.js:364
#: ../js/misc/util.js:143 ../js/ui/runDialog.js:351
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Esecuzione di «%s» non riuscita:"
@ -344,7 +348,7 @@ msgstr "Rimuovi"
#: ../js/ui/docDisplay.js:18
msgid "RECENT ITEMS"
msgstr "Elementi recenti"
msgstr "ELEMENTI RECENTI"
#: ../js/ui/endSessionDialog.js:63
#, c-format
@ -358,7 +362,9 @@ msgstr "Termina sessione"
# oddio... abbandonare il sistema sembra la nave che affonda... (LF)
#: ../js/ui/endSessionDialog.js:65
msgid "Click Log Out to quit these applications and log out of the system."
msgstr "Fare clic su «Termina sessione» per chiudere queste applicazioni e abbandonare il sistema."
msgstr ""
"Fare clic su «Termina sessione» per chiudere queste applicazioni e "
"abbandonare il sistema."
#: ../js/ui/endSessionDialog.js:66
#, c-format
@ -381,7 +387,9 @@ msgstr "Arresta"
# usato un termine diverso, magari si capisce meglio (LF)
#: ../js/ui/endSessionDialog.js:75
msgid "Click Shut Down to quit these applications and shut down the system."
msgstr "Fare clic su «Arresta» per chiudere queste applicazioni e spegnere il sistema."
msgstr ""
"Fare clic su «Arresta» per chiudere queste applicazioni e spegnere il "
"sistema."
#: ../js/ui/endSessionDialog.js:76
#, c-format
@ -398,7 +406,9 @@ msgstr "Riavvia"
#: ../js/ui/endSessionDialog.js:85
msgid "Click Restart to quit these applications and restart the system."
msgstr "Fare clic su «Riavvia» per chiudere queste applicazioni e riavviare il sistema."
msgstr ""
"Fare clic su «Riavvia» per chiudere queste applicazioni e riavviare il "
"sistema."
#: ../js/ui/endSessionDialog.js:86
#, c-format
@ -413,7 +423,7 @@ msgstr "Riavvio del sistema."
msgid "Confirm"
msgstr "Conferma"
#: ../js/ui/endSessionDialog.js:400 ../js/ui/status/bluetooth.js:469
#: ../js/ui/endSessionDialog.js:400 ../js/ui/status/bluetooth.js:470
msgid "Cancel"
msgstr "Annulla"
@ -449,7 +459,7 @@ msgstr "Visualizza sorgente"
msgid "Web Page"
msgstr "Pagina web"
#: ../js/ui/messageTray.js:1748
#: ../js/ui/messageTray.js:1765
msgid "System Information"
msgstr "Informazione di sistema"
@ -519,22 +529,22 @@ msgid "Activities"
msgstr "Attività"
# (ndt) libera, ma unmount non si può proprio vedere...
#: ../js/ui/placeDisplay.js:112
#: ../js/ui/placeDisplay.js:106
#, c-format
msgid "Failed to unmount '%s'"
msgstr "Impossibile scollegare «%s»"
#: ../js/ui/placeDisplay.js:115
#: ../js/ui/placeDisplay.js:109
msgid "Retry"
msgstr "Riprova"
#: ../js/ui/placeDisplay.js:160
#: ../js/ui/placeDisplay.js:150
msgid "Connect to..."
msgstr "Connetti a..."
#: ../js/ui/placeDisplay.js:559
#: ../js/ui/placeDisplay.js:386
msgid "PLACES & DEVICES"
msgstr "Risorse e dispositivi"
msgstr "RISORSE E DISPOSITIVI"
#. Translators: this MUST be either "toggle-switch-us"
#. (for toggle switches containing the English words
@ -545,7 +555,7 @@ msgstr "Risorse e dispositivi"
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
#: ../js/ui/runDialog.js:222
#: ../js/ui/runDialog.js:209
msgid "Please enter a command:"
msgstr "Inserire un comando:"
@ -629,7 +639,7 @@ msgstr "Contrasto elevato"
msgid "Large Text"
msgstr "Caratteri grandi"
#: ../js/ui/status/bluetooth.js:42 ../js/ui/status/bluetooth.js:240
#: ../js/ui/status/bluetooth.js:42 ../js/ui/status/bluetooth.js:241
msgid "Bluetooth"
msgstr "Bluetooth"
@ -646,105 +656,105 @@ msgstr "Invia file al dispositivo..."
msgid "Setup a New Device..."
msgstr "Imposta un nuovo dispositivo..."
#: ../js/ui/status/bluetooth.js:94
#: ../js/ui/status/bluetooth.js:95
msgid "Bluetooth Settings"
msgstr "Impostazioni Bluetooth"
# indica lo stato del device BT, per esempio gli auricolari
# credo sia meglio l'aggettivo che il sostantivo
#: ../js/ui/status/bluetooth.js:191
#: ../js/ui/status/bluetooth.js:192
msgid "Connection"
msgstr "Collegato"
#: ../js/ui/status/bluetooth.js:227
#: ../js/ui/status/bluetooth.js:228
msgid "Send Files..."
msgstr "Invia file..."
#: ../js/ui/status/bluetooth.js:232
#: ../js/ui/status/bluetooth.js:233
msgid "Browse Files..."
msgstr "Esplora file..."
#: ../js/ui/status/bluetooth.js:241
#: ../js/ui/status/bluetooth.js:242
msgid "Error browsing device"
msgstr "Errore nell'esplorare il dispositivo"
#: ../js/ui/status/bluetooth.js:242
#: ../js/ui/status/bluetooth.js:243
#, c-format
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "Non è possibile esplorare il dispositivo richiesto, l'errore è «%s»"
#: ../js/ui/status/bluetooth.js:250 ../js/ui/status/keyboard.js:78
#: ../js/ui/status/bluetooth.js:251 ../js/ui/status/keyboard.js:78
msgid "Keyboard Settings"
msgstr "Impostazioni tastiera"
#: ../js/ui/status/bluetooth.js:255
#: ../js/ui/status/bluetooth.js:256
msgid "Mouse Settings"
msgstr "Impostazioni mouse"
#: ../js/ui/status/bluetooth.js:262 ../js/ui/status/volume.js:63
#: ../js/ui/status/bluetooth.js:263 ../js/ui/status/volume.js:63
msgid "Sound Settings"
msgstr "Impostazioni audio"
#: ../js/ui/status/bluetooth.js:336 ../js/ui/status/bluetooth.js:370
#: ../js/ui/status/bluetooth.js:410 ../js/ui/status/bluetooth.js:443
#: ../js/ui/status/bluetooth.js:337 ../js/ui/status/bluetooth.js:371
#: ../js/ui/status/bluetooth.js:411 ../js/ui/status/bluetooth.js:444
msgid "Bluetooth Agent"
msgstr ""
#: ../js/ui/status/bluetooth.js:371
#: ../js/ui/status/bluetooth.js:372
#, c-format
msgid "Authorization request from %s"
msgstr "Richesta autorizzazione da %s"
#: ../js/ui/status/bluetooth.js:377
#: ../js/ui/status/bluetooth.js:378
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "Il dispositivo %s vuole accedere al servizio «%s»"
#: ../js/ui/status/bluetooth.js:379
#: ../js/ui/status/bluetooth.js:380
msgid "Always grant access"
msgstr "Consenti sempre accesso"
#: ../js/ui/status/bluetooth.js:380
#: ../js/ui/status/bluetooth.js:381
msgid "Grant this time only"
msgstr "Consenti solo stavolta"
#: ../js/ui/status/bluetooth.js:381
#: ../js/ui/status/bluetooth.js:382
msgid "Reject"
msgstr "Rifiuta"
#: ../js/ui/status/bluetooth.js:411
#: ../js/ui/status/bluetooth.js:412
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Conferma associazione per %s"
#: ../js/ui/status/bluetooth.js:417 ../js/ui/status/bluetooth.js:451
#: ../js/ui/status/bluetooth.js:418 ../js/ui/status/bluetooth.js:452
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "Il dispositivo %s vuole associarsi con questo computer"
#: ../js/ui/status/bluetooth.js:418
#: ../js/ui/status/bluetooth.js:419
#, c-format
msgid "Please confirm whether the PIN '%s' matches the one on the device."
msgstr "Confermare la corrispondenza del PIN «%s» con quello sul dispositivo."
#: ../js/ui/status/bluetooth.js:420
#: ../js/ui/status/bluetooth.js:421
msgid "Matches"
msgstr "Corrisponde"
#: ../js/ui/status/bluetooth.js:421
#: ../js/ui/status/bluetooth.js:422
msgid "Does not match"
msgstr "Non corrisponde"
#: ../js/ui/status/bluetooth.js:444
#: ../js/ui/status/bluetooth.js:445
#, c-format
msgid "Pairing request for %s"
msgstr "Richiesta associazione per %s"
#: ../js/ui/status/bluetooth.js:452
#: ../js/ui/status/bluetooth.js:453
msgid "Please enter the PIN mentioned on the device."
msgstr "Inserire il PIN indicato sul dispositivo."
#: ../js/ui/status/bluetooth.js:468
#: ../js/ui/status/bluetooth.js:469
msgid "OK"
msgstr "OK"
@ -867,7 +877,7 @@ msgstr "%s non è disponibile."
#: ../js/ui/telepathyClient.js:666
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Inviato alle %-H.%M.%S di %A"
msgstr "Inviato alle %-H.%M di %A"
# FIXME ma ha senso in inglese???
#: ../js/ui/viewSelector.js:26
@ -919,32 +929,32 @@ msgstr[1] "%u ingressi"
msgid "System Sounds"
msgstr "Audio di sistema"
#: ../src/shell-global.c:1233
#: ../src/shell-global.c:1366
msgid "Less than a minute ago"
msgstr "Meno di un minuto fa"
#: ../src/shell-global.c:1237
#: ../src/shell-global.c:1370
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuto fa"
msgstr[1] "%d minuti fa"
#: ../src/shell-global.c:1242
#: ../src/shell-global.c:1375
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d ora fa"
msgstr[1] "%d ore fa"
#: ../src/shell-global.c:1247
#: ../src/shell-global.c:1380
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d giorno fa"
msgstr[1] "%d giorni fa"
#: ../src/shell-global.c:1252
#: ../src/shell-global.c:1385
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"

View File

@ -159,11 +159,17 @@ st_source_c = \
st/st-widget.c \
$(NULL)
st_non_gir_sources = \
st/st-scroll-view-fade.c \
st/st-scroll-view-fade.h \
$(NULL)
noinst_LTLIBRARIES += libst-1.0.la
libst_1_0_la_LIBADD = -lm $(ST_LIBS)
libst_1_0_la_SOURCES = \
$(st_source_c) \
$(st_source_c) \
$(st_non_gir_sources) \
$(st_source_private_h) \
$(st_source_private_c) \
$(st_source_h) \

View File

@ -262,7 +262,7 @@ St-1.0.gir: $(mutter) $(G_IR_SCANNER) libst-1.0.la Makefile
--libtool="$(LIBTOOL)" \
--library=libst-1.0.la \
-DST_COMPILATION \
$(filter-out %-private.h, $(addprefix $(srcdir)/,$(st_source_h))) \
$(filter-out %-private.h $(st_non_gir_sources), $(addprefix $(srcdir)/,$(st_source_h))) \
$(addprefix $(srcdir)/,$(st_source_c)) \
$(srcdir)/st-enum-types.h \
$(st_cflags) \

View File

@ -1020,13 +1020,10 @@ on_get_unix_user_finished (DBusGProxy *proxy,
DBusGProxyCall *call,
GdmUserManagerNewSession *new_session)
{
GdmUserManager *manager;
GError *error;
guint uid;
gboolean res;
manager = new_session->manager;
g_assert (new_session->get_unix_user_call == call);
error = NULL;
@ -1550,11 +1547,9 @@ static void
get_accounts_proxy (GdmUserManager *manager)
{
DBusGProxy *proxy;
GError *error;
g_assert (manager->priv->accounts_proxy == NULL);
error = NULL;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
ACCOUNTS_NAME,
ACCOUNTS_PATH,
@ -2554,8 +2549,6 @@ reload_shells (GdmUserManager *manager)
static void
load_users_manually (GdmUserManager *manager)
{
gboolean res;
manager->priv->shells = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
@ -2564,7 +2557,7 @@ load_users_manually (GdmUserManager *manager)
load_sessions (manager);
res = load_ck_history (manager);
load_ck_history (manager);
schedule_reload_passwd (manager);
}
@ -2605,10 +2598,8 @@ load_seat_incrementally (GdmUserManager *manager)
}
if (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_LOADED) {
gboolean res;
load_sessions (manager);
res = load_ck_history (manager);
load_ck_history (manager);
}
maybe_set_is_loaded (manager);

View File

@ -151,10 +151,6 @@ gdm_user_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
GdmUser *user;
user = GDM_USER (object);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);

View File

@ -59,13 +59,10 @@ update_settings (GvcMixerEventRole *role,
gpointer *op)
{
pa_operation *o;
guint index;
const GvcChannelMap *map;
pa_context *context;
pa_ext_stream_restore_info info;
index = gvc_mixer_stream_get_index (GVC_MIXER_STREAM (role));
map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role));
info.volume = *gvc_channel_map_get_cvolume(map);
@ -165,12 +162,9 @@ gvc_mixer_event_role_constructor (GType type,
GObjectConstructParam *construct_params)
{
GObject *object;
GvcMixerEventRole *self;
object = G_OBJECT_CLASS (gvc_mixer_event_role_parent_class)->constructor (type, n_construct_properties, construct_params);
self = GVC_MIXER_EVENT_ROLE (object);
return object;
}

View File

@ -55,12 +55,10 @@ gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
const GvcChannelMap *map;
pa_context *context;
const pa_cvolume *cv;
guint num_channels;
index = gvc_mixer_stream_get_index (stream);
map = gvc_mixer_stream_get_channel_map (stream);
num_channels = gvc_channel_map_get_num_channels (map);
cv = gvc_channel_map_get_cvolume(map);
@ -115,12 +113,9 @@ gvc_mixer_sink_input_constructor (GType type,
GObjectConstructParam *construct_params)
{
GObject *object;
GvcMixerSinkInput *self;
object = G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->constructor (type, n_construct_properties, construct_params);
self = GVC_MIXER_SINK_INPUT (object);
return object;
}
@ -149,13 +144,9 @@ gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
static void
gvc_mixer_sink_input_dispose (GObject *object)
{
GvcMixerSinkInput *mixer_sink_input;
g_return_if_fail (object != NULL);
g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));
mixer_sink_input = GVC_MIXER_SINK_INPUT (object);
G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->dispose (object);
}

View File

@ -145,12 +145,9 @@ gvc_mixer_sink_constructor (GType type,
GObjectConstructParam *construct_params)
{
GObject *object;
GvcMixerSink *self;
object = G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->constructor (type, n_construct_properties, construct_params);
self = GVC_MIXER_SINK (object);
return object;
}
@ -180,13 +177,9 @@ gvc_mixer_sink_init (GvcMixerSink *sink)
static void
gvc_mixer_sink_dispose (GObject *object)
{
GvcMixerSink *mixer_sink;
g_return_if_fail (object != NULL);
g_return_if_fail (GVC_IS_MIXER_SINK (object));
mixer_sink = GVC_MIXER_SINK (object);
G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->dispose (object);
}

View File

@ -66,12 +66,9 @@ gvc_mixer_source_output_constructor (GType type,
GObjectConstructParam *construct_params)
{
GObject *object;
GvcMixerSourceOutput *self;
object = G_OBJECT_CLASS (gvc_mixer_source_output_parent_class)->constructor (type, n_construct_properties, construct_params);
self = GVC_MIXER_SOURCE_OUTPUT (object);
return object;
}

View File

@ -145,12 +145,9 @@ gvc_mixer_source_constructor (GType type,
GObjectConstructParam *construct_params)
{
GObject *object;
GvcMixerSource *self;
object = G_OBJECT_CLASS (gvc_mixer_source_parent_class)->constructor (type, n_construct_properties, construct_params);
self = GVC_MIXER_SOURCE (object);
return object;
}
@ -180,13 +177,9 @@ gvc_mixer_source_init (GvcMixerSource *source)
static void
gvc_mixer_source_dispose (GObject *object)
{
GvcMixerSource *mixer_source;
g_return_if_fail (object != NULL);
g_return_if_fail (GVC_IS_MIXER_SOURCE (object));
mixer_source = GVC_MIXER_SOURCE (object);
G_OBJECT_CLASS (gvc_mixer_source_parent_class)->dispose (object);
}

View File

@ -1317,7 +1317,6 @@ shell_app_info_launch_full (ShellAppInfo *info,
gboolean ret;
ShellGlobal *global;
MetaScreen *screen;
MetaDisplay *display;
if (startup_id)
*startup_id = NULL;
@ -1353,7 +1352,6 @@ shell_app_info_launch_full (ShellAppInfo *info,
global = shell_global_get ();
screen = shell_global_get_screen (global);
display = meta_screen_get_display (screen);
if (timestamp == 0)
timestamp = clutter_get_current_event_time ();

View File

@ -324,7 +324,8 @@ on_app_state_changed (ShellWindowTracker *tracker,
running = shell_app_get_state (app) == SHELL_APP_STATE_RUNNING;
usage->last_seen = get_time ();
if (running)
usage->last_seen = get_time ();
}
static void

View File

@ -285,7 +285,8 @@ shell_doc_system_open (ShellDocSystem *system,
app_exec_quoted = g_regex_replace (regex, app_exec, -1, 0, "%%", 0, NULL);
g_regex_unref (regex);
app_info = g_app_info_create_from_commandline (app_exec, NULL, 0, NULL);
app_info = g_app_info_create_from_commandline (app_exec_quoted, NULL, 0, NULL);
g_free (app_exec_quoted);
/* The point of passing an app launch context to
launch() is mostly to get startup notification and

View File

@ -885,7 +885,6 @@ shell_global_add_extension_importer (ShellGlobal *global,
GError **error)
{
jsval target_object;
JSObject *importer;
JSContext *context = gjs_context_get_native_context (global->js_context);
char *search_path[2] = { 0, 0 };
@ -920,7 +919,7 @@ shell_global_add_extension_importer (ShellGlobal *global,
}
search_path[0] = (char*)directory;
importer = gjs_define_importer (context, JSVAL_TO_OBJECT (target_object), target_property, (const char **)search_path, FALSE);
gjs_define_importer (context, JSVAL_TO_OBJECT (target_object), target_property, (const char **)search_path, FALSE);
JS_EndRequest (context);
return TRUE;
out_error:

View File

@ -83,6 +83,7 @@ struct _ShellRecorder {
guint redraw_idle;
guint update_memory_used_timeout;
guint update_pointer_timeout;
guint repaint_hook_id;
};
struct _RecorderPipeline
@ -146,11 +147,11 @@ G_DEFINE_TYPE(ShellRecorder, shell_recorder, G_TYPE_OBJECT);
* (Theora does have some support for frames at non-uniform times, but
* things seem to break down if there are large gaps.)
*/
#define DEFAULT_PIPELINE "videorate ! theoraenc ! oggmux"
#define DEFAULT_PIPELINE "videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux"
/* The default filename pattern. Example shell-20090311b-2.ogg
/* The default filename pattern. Example shell-20090311b-2.webm
*/
#define DEFAULT_FILENAME "shell-%d%u-%c.ogg"
#define DEFAULT_FILENAME "shell-%d%u-%c.webm"
/* If we can find the amount of memory on the machine, we use half
* of that for memory_target, otherwise, we use this value, in kB.
@ -239,6 +240,22 @@ get_memory_target (void)
return DEFAULT_MEMORY_TARGET;
}
/*
* Used to force full stage redraws during recording to avoid artifacts
*
* Note: That this will cause the stage to be repainted on
* every animation frame even if the frame wouldn't normally cause any new
* drawing
*/
static gboolean
recorder_repaint_hook (gpointer data)
{
ClutterActor *stage = data;
clutter_actor_queue_redraw (stage);
return TRUE;
}
static void
shell_recorder_init (ShellRecorder *recorder)
{
@ -1476,11 +1493,47 @@ recorder_pipeline_closed (RecorderPipeline *pipeline)
recorder_pipeline_free (pipeline);
}
/*
* Replaces '%T' in the passed pipeline with the thread count,
* the maximum possible value is 64 (limit of what vp8enc supports)
*
* It is assumes that %T occurs only once.
*/
static char*
substitute_thread_count (const char *pipeline)
{
char *tmp;
int n_threads;
GString *result;
tmp = strstr (pipeline, "%T");
if (!tmp)
return g_strdup (pipeline);
#ifdef _SC_NPROCESSORS_ONLN
{
int n_processors = sysconf (_SC_NPROCESSORS_ONLN); /* includes hyper-threading */
n_threads = MIN (MAX (1, n_processors - 1), 64);
}
#else
n_threads = 3;
#endif
result = g_string_new (NULL);
g_string_append_len (result, pipeline, tmp - pipeline);
g_string_append_printf (result, "%d", n_threads);
g_string_append (result, tmp + 2);
return g_string_free (result, FALSE);;
}
static gboolean
recorder_open_pipeline (ShellRecorder *recorder)
{
RecorderPipeline *pipeline;
const char *pipeline_description;
char *parsed_pipeline;
GError *error = NULL;
GstBus *bus;
@ -1492,9 +1545,12 @@ recorder_open_pipeline (ShellRecorder *recorder)
if (!pipeline_description)
pipeline_description = DEFAULT_PIPELINE;
pipeline->pipeline = gst_parse_launch_full (pipeline_description, NULL,
parsed_pipeline = substitute_thread_count (pipeline_description);
pipeline->pipeline = gst_parse_launch_full (parsed_pipeline, NULL,
GST_PARSE_FLAG_FATAL_ERRORS,
&error);
g_free (parsed_pipeline);
if (pipeline->pipeline == NULL)
{
@ -1682,6 +1738,9 @@ shell_recorder_record (ShellRecorder *recorder)
recorder->state = RECORDER_STATE_RECORDING;
recorder_add_update_pointer_timeout (recorder);
/* Set up repaint hook */
recorder->repaint_hook_id = clutter_threads_add_repaint_func(recorder_repaint_hook, recorder->stage, NULL);
/* Record an initial frame and also redraw with the indicator */
clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
@ -1723,6 +1782,12 @@ shell_recorder_pause (ShellRecorder *recorder)
/* Queue a redraw to remove the recording indicator */
clutter_actor_queue_redraw (CLUTTER_ACTOR (recorder->stage));
if (recorder->repaint_hook_id != 0)
{
clutter_threads_remove_repaint_func (recorder->repaint_hook_id);
recorder->repaint_hook_id = 0;
}
}
/**

View File

@ -825,12 +825,9 @@ on_focus_window_changed (MetaDisplay *display,
GParamSpec *spec,
ShellWindowTracker *tracker)
{
MetaScreen *screen;
MetaWindow *new_focus_win;
ShellApp *new_focus_app;
screen = shell_global_get_screen (shell_global_get ());
new_focus_win = meta_display_get_focus_window (display);
new_focus_app = new_focus_win ? g_hash_table_lookup (tracker->window_to_app, new_focus_win) : NULL;

View File

@ -62,6 +62,7 @@ struct _StIconPrivate
CoglHandle shadow_material;
float shadow_width;
float shadow_height;
StShadow *shadow_spec;
};
static void st_icon_update (StIcon *icon);
@ -164,6 +165,12 @@ st_icon_dispose (GObject *gobject)
priv->shadow_material = COGL_INVALID_HANDLE;
}
if (priv->shadow_spec)
{
st_shadow_unref (priv->shadow_spec);
priv->shadow_spec = NULL;
}
G_OBJECT_CLASS (st_icon_parent_class)->dispose (gobject);
}
@ -249,8 +256,6 @@ st_icon_paint (ClutterActor *actor)
{
if (priv->shadow_material)
{
StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor));
StShadow *shadow_spec = st_theme_node_get_shadow (node);
ClutterActorBox allocation;
float width, height;
@ -262,7 +267,7 @@ st_icon_paint (ClutterActor *actor)
allocation.x2 = allocation.x1 + priv->shadow_width;
allocation.y2 = allocation.y1 + priv->shadow_height;
_st_paint_shadow_with_opacity (shadow_spec,
_st_paint_shadow_with_opacity (priv->shadow_spec,
priv->shadow_material,
&allocation,
clutter_actor_get_paint_opacity (priv->icon_texture));
@ -279,6 +284,13 @@ st_icon_style_changed (StWidget *widget)
StThemeNode *theme_node = st_widget_get_theme_node (widget);
StIconPrivate *priv = self->priv;
if (priv->shadow_spec)
{
st_shadow_unref (priv->shadow_spec);
priv->shadow_spec = NULL;
}
priv->shadow_spec = st_theme_node_get_shadow (theme_node, "icon-shadow");
priv->theme_icon_size = (int)(0.5 + st_theme_node_get_length (theme_node, "icon-size"));
st_icon_update_icon_size (self);
st_icon_update (self);
@ -353,8 +365,6 @@ static void
st_icon_update_shadow_material (StIcon *icon)
{
StIconPrivate *priv = icon->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (icon));
StShadow *shadow_spec = st_theme_node_get_shadow (theme_node);
if (priv->shadow_material)
{
@ -362,14 +372,15 @@ st_icon_update_shadow_material (StIcon *icon)
priv->shadow_material = COGL_INVALID_HANDLE;
}
if (shadow_spec)
if (priv->shadow_spec)
{
CoglHandle material;
gint width, height;
clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->icon_texture),
&width, &height);
material = _st_create_shadow_material_from_actor (shadow_spec,
material = _st_create_shadow_material_from_actor (priv->shadow_spec,
priv->icon_texture);
priv->shadow_material = material;
priv->shadow_width = width;

View File

@ -250,7 +250,7 @@ st_overflow_box_allocate (ClutterActor *actor,
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
ClutterActorBox content_box;
gfloat position;
float avail_width, avail_height;
float avail_width;
GList *l, *children;
int i;
gboolean done_non_fixed;
@ -265,7 +265,6 @@ st_overflow_box_allocate (ClutterActor *actor,
st_theme_node_get_content_box (theme_node, box, &content_box);
avail_width = content_box.x2 - content_box.x1;
avail_height = content_box.y2 - content_box.y1;
position = content_box.y1;
priv->n_visible = 0;

View File

@ -49,7 +49,7 @@ _st_actor_get_preferred_width (ClutterActor *actor,
ClutterRequestMode mode;
gfloat natural_height;
g_object_get (G_OBJECT (actor), "request-mode", &mode, NULL);
mode = clutter_actor_get_request_mode (actor);
if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
{
clutter_actor_get_preferred_height (actor, -1, NULL, &natural_height);
@ -86,7 +86,7 @@ _st_actor_get_preferred_height (ClutterActor *actor,
ClutterRequestMode mode;
gfloat natural_width;
g_object_get (G_OBJECT (actor), "request-mode", &mode, NULL);
mode = clutter_actor_get_request_mode (actor);
if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
clutter_actor_get_preferred_width (actor, -1, NULL, &natural_width);
@ -173,8 +173,7 @@ _st_allocate_fill (StWidget *parent,
* modified to cope with the fact that the available size may be
* less than the preferred size.
*/
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
request = clutter_actor_get_request_mode (child);
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
@ -423,44 +422,31 @@ calculate_gaussian_kernel (gdouble sigma,
return ret;
}
CoglHandle
_st_create_shadow_material (StShadow *shadow_spec,
CoglHandle src_texture)
static guchar *
blur_pixels (guchar *pixels_in,
gint width_in,
gint height_in,
gint rowstride_in,
gdouble blur,
gint *width_out,
gint *height_out,
gint *rowstride_out)
{
static CoglHandle shadow_material_template = COGL_INVALID_HANDLE;
CoglHandle material;
CoglHandle texture;
guchar *pixels_in, *pixels_out;
gint width_in, height_in, rowstride_in;
gint width_out, height_out, rowstride_out;
float sigma;
g_return_val_if_fail (shadow_spec != NULL, COGL_INVALID_HANDLE);
g_return_val_if_fail (src_texture != COGL_INVALID_HANDLE,
COGL_INVALID_HANDLE);
guchar *pixels_out;
float sigma;
/* we use an approximation of the sigma - blur radius relationship used
in Firefox for doing SVG blurs; see
http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/src/gfxBlur.cpp#280
*/
sigma = shadow_spec->blur / 1.9;
sigma = blur / 1.9;
width_in = cogl_texture_get_width (src_texture);
height_in = cogl_texture_get_height (src_texture);
rowstride_in = (width_in + 3) & ~3;
pixels_in = g_malloc0 (rowstride_in * height_in);
cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8,
rowstride_in, pixels_in);
if ((guint) shadow_spec->blur == 0)
if ((guint) blur == 0)
{
width_out = width_in;
height_out = height_in;
rowstride_out = rowstride_in;
pixels_out = g_memdup (pixels_in, rowstride_out * height_out);
*width_out = width_in;
*height_out = height_in;
*rowstride_out = rowstride_in;
pixels_out = g_memdup (pixels_in, *rowstride_out * *height_out);
}
else
{
@ -472,18 +458,18 @@ _st_create_shadow_material (StShadow *shadow_spec,
n_values = (gint) 5 * sigma;
half = n_values / 2;
width_out = width_in + 2 * half;
height_out = height_in + 2 * half;
rowstride_out = (width_out + 3) & ~3;
*width_out = width_in + 2 * half;
*height_out = height_in + 2 * half;
*rowstride_out = (*width_out + 3) & ~3;
pixels_out = g_malloc0 (rowstride_out * height_out);
line = g_malloc0 (rowstride_out);
pixels_out = g_malloc0 (*rowstride_out * *height_out);
line = g_malloc0 (*rowstride_out);
kernel = calculate_gaussian_kernel (sigma, n_values);
/* vertical blur */
for (x_in = 0; x_in < width_in; x_in++)
for (y_out = 0; y_out < height_out; y_out++)
for (y_out = 0; y_out < *height_out; y_out++)
{
guchar *pixel_in, *pixel_out;
gint i0, i1;
@ -497,7 +483,7 @@ _st_create_shadow_material (StShadow *shadow_spec,
i1 = MIN (height_in + half - y_in, n_values);
pixel_in = pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
pixel_out = pixels_out + y_out * rowstride_out + (x_in + half);
pixel_out = pixels_out + y_out * *rowstride_out + (x_in + half);
for (i = i0; i < i1; i++)
{
@ -507,11 +493,11 @@ _st_create_shadow_material (StShadow *shadow_spec,
}
/* horizontal blur */
for (y_out = 0; y_out < height_out; y_out++)
for (y_out = 0; y_out < *height_out; y_out++)
{
memcpy (line, pixels_out + y_out * rowstride_out, rowstride_out);
memcpy (line, pixels_out + y_out * *rowstride_out, *rowstride_out);
for (x_out = 0; x_out < width_out; x_out++)
for (x_out = 0; x_out < *width_out; x_out++)
{
gint i0, i1;
guchar *pixel_out, *pixel_in;
@ -520,10 +506,10 @@ _st_create_shadow_material (StShadow *shadow_spec,
* full i range [0, n_values) so that x is in [0, width_out).
*/
i0 = MAX (half - x_out, 0);
i1 = MIN (width_out + half - x_out, n_values);
i1 = MIN (*width_out + half - x_out, n_values);
pixel_in = line + x_out + i0 - half;
pixel_out = pixels_out + rowstride_out * y_out + x_out;
pixel_out = pixels_out + *rowstride_out * y_out + x_out;
*pixel_out = 0;
for (i = i0; i < i1; i++)
@ -537,6 +523,39 @@ _st_create_shadow_material (StShadow *shadow_spec,
g_free (line);
}
return pixels_out;
}
CoglHandle
_st_create_shadow_material (StShadow *shadow_spec,
CoglHandle src_texture)
{
static CoglHandle shadow_material_template = COGL_INVALID_HANDLE;
CoglHandle material;
CoglHandle texture;
guchar *pixels_in, *pixels_out;
gint width_in, height_in, rowstride_in;
gint width_out, height_out, rowstride_out;
g_return_val_if_fail (shadow_spec != NULL, COGL_INVALID_HANDLE);
g_return_val_if_fail (src_texture != COGL_INVALID_HANDLE,
COGL_INVALID_HANDLE);
width_in = cogl_texture_get_width (src_texture);
height_in = cogl_texture_get_height (src_texture);
rowstride_in = (width_in + 3) & ~3;
pixels_in = g_malloc0 (rowstride_in * height_in);
cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8,
rowstride_in, pixels_in);
pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in,
shadow_spec->blur,
&width_out, &height_out, &rowstride_out);
g_free (pixels_in);
texture = cogl_texture_new_from_data (width_out,
height_out,
COGL_TEXTURE_NONE,
@ -545,7 +564,6 @@ _st_create_shadow_material (StShadow *shadow_spec,
rowstride_out,
pixels_out);
g_free (pixels_in);
g_free (pixels_out);
if (G_UNLIKELY (shadow_material_template == COGL_INVALID_HANDLE))
@ -618,6 +636,103 @@ _st_create_shadow_material_from_actor (StShadow *shadow_spec,
return shadow_material;
}
cairo_pattern_t *
_st_create_shadow_cairo_pattern (StShadow *shadow_spec,
cairo_pattern_t *src_pattern)
{
cairo_t *cr;
cairo_surface_t *src_surface;
cairo_surface_t *surface_in;
cairo_surface_t *surface_out;
cairo_pattern_t *dst_pattern;
guchar *pixels_in, *pixels_out;
gint width_in, height_in, rowstride_in;
gint width_out, height_out, rowstride_out;
cairo_matrix_t shadow_matrix;
g_return_val_if_fail (shadow_spec != NULL, NULL);
g_return_val_if_fail (src_pattern != NULL, NULL);
cairo_pattern_get_surface (src_pattern, &src_surface);
width_in = cairo_image_surface_get_width (src_surface);
height_in = cairo_image_surface_get_height (src_surface);
/* We want the output to be a color agnostic alpha mask,
* so we need to strip the color channels from the input
*/
if (cairo_image_surface_get_format (src_surface) != CAIRO_FORMAT_A8)
{
surface_in = cairo_image_surface_create (CAIRO_FORMAT_A8,
width_in, height_in);
cr = cairo_create (surface_in);
cairo_set_source_surface (cr, src_surface, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
}
else
{
surface_in = cairo_surface_reference (src_surface);
}
pixels_in = cairo_image_surface_get_data (surface_in);
rowstride_in = cairo_image_surface_get_stride (surface_in);
pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in,
shadow_spec->blur,
&width_out, &height_out, &rowstride_out);
cairo_surface_destroy (surface_in);
surface_out = cairo_image_surface_create_for_data (pixels_out,
CAIRO_FORMAT_A8,
width_out,
height_out,
rowstride_out);
dst_pattern = cairo_pattern_create_for_surface (surface_out);
cairo_surface_destroy (surface_out);
cairo_pattern_get_matrix (src_pattern, &shadow_matrix);
/* Read all the code from the cairo_pattern_set_matrix call
* at the end of this function to here from bottom to top,
* because each new affine transformation is applied in
* front of all the previous ones */
/* 6. Invert the matrix back */
cairo_matrix_invert (&shadow_matrix);
/* 5. Adjust based on specified offsets */
cairo_matrix_translate (&shadow_matrix,
shadow_spec->xoffset,
shadow_spec->yoffset);
/* 4. Recenter the newly scaled image */
cairo_matrix_translate (&shadow_matrix,
- shadow_spec->spread,
- shadow_spec->spread);
/* 3. Scale up the blurred image to fill the spread */
cairo_matrix_scale (&shadow_matrix,
(width_in + 2.0 * shadow_spec->spread) / width_in,
(height_in + 2.0 * shadow_spec->spread) / height_in);
/* 2. Shift the blurred image left, so that it aligns centered
* under the unblurred one */
cairo_matrix_translate (&shadow_matrix,
- (width_out - width_in) / 2.0,
- (height_out - height_in) / 2.0);
/* 1. Invert the matrix so we can work with it in pattern space
*/
cairo_matrix_invert (&shadow_matrix);
cairo_pattern_set_matrix (dst_pattern, &shadow_matrix);
return dst_pattern;
}
void
_st_paint_shadow_with_opacity (StShadow *shadow_spec,
CoglHandle shadow_material,

View File

@ -24,6 +24,7 @@
#define __ST_PRIVATE_H__
#include <glib.h>
#include <cairo.h>
#include "st-widget.h"
#include "st-bin.h"
#include "st-shadow.h"
@ -79,6 +80,9 @@ CoglHandle _st_create_shadow_material (StShadow *shadow_spec,
CoglHandle src_texture);
CoglHandle _st_create_shadow_material_from_actor (StShadow *shadow_spec,
ClutterActor *actor);
cairo_pattern_t *_st_create_shadow_cairo_pattern (StShadow *shadow_spec,
cairo_pattern_t *src_pattern);
void _st_paint_shadow_with_opacity (StShadow *shadow_spec,
CoglHandle shadow_material,
ClutterActorBox *box,

View File

@ -643,13 +643,11 @@ st_scroll_bar_constructor (GType type,
GObjectClass *gobject_class;
GObject *obj;
StScrollBar *bar;
StScrollBarPrivate *priv;
gobject_class = G_OBJECT_CLASS (st_scroll_bar_parent_class);
obj = gobject_class->constructor (type, n_properties, properties);
bar = ST_SCROLL_BAR (obj);
priv = ST_SCROLL_BAR_GET_PRIVATE (bar);
g_signal_connect (bar, "notify::reactive",
G_CALLBACK (bar_reactive_notify_cb), NULL);

View File

@ -0,0 +1,348 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scroll-view-fade.h: Edge fade effect for StScrollView
*
* Copyright 2010 Intel Corporation.
* Copyright 2011 Adel Gadllah
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define ST_SCROLL_VIEW_FADE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass))
#define ST_IS_SCROLL_VIEW_FADE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW_FADE))
#define ST_SCROLL_VIEW_FADE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass))
#include "st-scroll-view-fade.h"
#include "st-scroll-view.h"
#include "st-scroll-bar.h"
#include "st-scrollable.h"
#include <clutter/clutter.h>
#include <cogl/cogl.h>
typedef struct _StScrollViewFadeClass StScrollViewFadeClass;
#define FADE_OFFSET 68.0f
static const gchar *fade_glsl_shader =
"uniform sampler2D tex;\n"
"uniform float height;\n"
"uniform float width;\n"
"uniform float scrollbar_width;\n"
"uniform float offset_bottom;\n"
"uniform float offset_top;\n"
"\n"
"void main ()\n"
"{\n"
" vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n"
" float y = height * cogl_tex_coord_in[0].y;\n"
" float x = width * cogl_tex_coord_in[0].x;\n"
" float ratio = 0.0;\n"
" float fade_bottom_start = height - offset_bottom;\n"
" \n"
" if (offset_top != 0.0 && y < offset_top && x < (width - scrollbar_width)) {\n"
" ratio = y / offset_top;\n"
" cogl_color_out = color * ratio;\n"
" }\n"
" else if (offset_bottom != 0.0 && y > fade_bottom_start && x < (width - scrollbar_width)) {\n"
" ratio = (height - y)/(height - fade_bottom_start);\n"
" cogl_color_out = color * ratio;\n"
" }\n"
" else { \n"
" cogl_color_out = color;\n"
" }\n"
"}\n";
struct _StScrollViewFade
{
ClutterOffscreenEffect parent_instance;
/* a back pointer to our actor, so that we can query it */
ClutterActor *actor;
CoglHandle shader;
CoglHandle program;
gint tex_uniform;
gint height_uniform;
gint width_uniform;
gint scrollbar_width_uniform;
gint offset_top_uniform;
gint offset_bottom_uniform;
StAdjustment *vadjustment;
guint is_attached : 1;
};
struct _StScrollViewFadeClass
{
ClutterOffscreenEffectClass parent_class;
};
G_DEFINE_TYPE (StScrollViewFade,
st_scroll_view_fade,
CLUTTER_TYPE_OFFSCREEN_EFFECT);
static gboolean
st_scroll_view_fade_pre_paint (ClutterEffect *effect)
{
StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
ClutterEffectClass *parent_class;
if (self->shader == COGL_INVALID_HANDLE)
return FALSE;
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
return FALSE;
if (self->actor == NULL)
return FALSE;
if (self->program == COGL_INVALID_HANDLE)
self->program = cogl_create_program ();
if (!self->is_attached)
{
g_assert (self->shader != COGL_INVALID_HANDLE);
g_assert (self->program != COGL_INVALID_HANDLE);
cogl_program_attach_shader (self->program, self->shader);
cogl_program_link (self->program);
cogl_handle_unref (self->shader);
self->is_attached = TRUE;
self->tex_uniform =
cogl_program_get_uniform_location (self->program, "tex");
self->height_uniform =
cogl_program_get_uniform_location (self->program, "height");
self->width_uniform =
cogl_program_get_uniform_location (self->program, "width");
self->scrollbar_width_uniform =
cogl_program_get_uniform_location (self->program, "scrollbar_width");
self->offset_top_uniform =
cogl_program_get_uniform_location (self->program, "offset_top");
self->offset_bottom_uniform =
cogl_program_get_uniform_location (self->program, "offset_bottom");
}
parent_class = CLUTTER_EFFECT_CLASS (st_scroll_view_fade_parent_class);
return parent_class->pre_paint (effect);
}
static CoglHandle
st_scroll_view_fade_create_texture (ClutterOffscreenEffect *effect,
gfloat min_width,
gfloat min_height)
{
return cogl_texture_new_with_size (min_width,
min_height,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
}
static void
st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect)
{
StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
ClutterOffscreenEffectClass *parent;
CoglHandle material;
gdouble value, lower, upper, page_size;
ClutterActor *vscroll = st_scroll_view_get_vscroll_bar (ST_SCROLL_VIEW (self->actor));
if (self->program == COGL_INVALID_HANDLE)
goto out;
st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
if (self->offset_top_uniform > -1) {
if (value > lower + 0.1)
cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, FADE_OFFSET);
else
cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, 0.0f);
}
if (self->offset_bottom_uniform > -1) {
if (value < upper - page_size - 0.1)
cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, FADE_OFFSET);
else
cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, 0.0f);
}
if (self->tex_uniform > -1)
cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
if (self->height_uniform > -1)
cogl_program_set_uniform_1f (self->program, self->height_uniform, clutter_actor_get_height (self->actor));
if (self->width_uniform > -1)
cogl_program_set_uniform_1f (self->program, self->width_uniform, clutter_actor_get_width (self->actor));
if (self->scrollbar_width_uniform > -1)
cogl_program_set_uniform_1f (self->program, self->scrollbar_width_uniform, clutter_actor_get_width (vscroll));
material = clutter_offscreen_effect_get_target (effect);
cogl_material_set_user_program (material, self->program);
out:
parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (st_scroll_view_fade_parent_class);
parent->paint_target (effect);
}
static void
on_vadjustment_changed (StAdjustment *adjustment,
ClutterEffect *effect)
{
gdouble value, lower, upper, page_size;
gboolean needs_fade;
st_adjustment_get_values (adjustment, &value, &lower, &upper, NULL, NULL, &page_size);
needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1);
clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), needs_fade);
}
static void
st_scroll_view_fade_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
StScrollViewFade *self = ST_SCROLL_VIEW_FADE (meta);
ClutterActorMetaClass *parent;
g_return_if_fail (actor == NULL || ST_IS_SCROLL_VIEW (actor));
if (self->shader == COGL_INVALID_HANDLE)
{
clutter_actor_meta_set_enabled (meta, FALSE);
return;
}
if (self->vadjustment)
{
g_signal_handlers_disconnect_by_func (self->vadjustment,
(gpointer)on_vadjustment_changed,
self);
self->vadjustment = NULL;
}
if (actor)
{
StScrollView *scroll_view = ST_SCROLL_VIEW (actor);
StScrollBar *vscroll = ST_SCROLL_BAR (st_scroll_view_get_vscroll_bar (scroll_view));
self->vadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (vscroll));
g_signal_connect (self->vadjustment, "changed",
G_CALLBACK (on_vadjustment_changed),
self);
on_vadjustment_changed (self->vadjustment, CLUTTER_EFFECT (self));
}
parent = CLUTTER_ACTOR_META_CLASS (st_scroll_view_fade_parent_class);
parent->set_actor (meta, actor);
/* we keep a back pointer here, to avoid going through the ActorMeta */
self->actor = clutter_actor_meta_get_actor (meta);
}
static void
st_scroll_view_fade_dispose (GObject *gobject)
{
StScrollViewFade *self = ST_SCROLL_VIEW_FADE (gobject);
if (self->program != COGL_INVALID_HANDLE)
{
cogl_handle_unref (self->program);
self->program = COGL_INVALID_HANDLE;
self->shader = COGL_INVALID_HANDLE;
}
if (self->vadjustment)
{
g_signal_handlers_disconnect_by_func (self->vadjustment,
(gpointer)on_vadjustment_changed,
self);
self->vadjustment = NULL;
}
self->actor = NULL;
G_OBJECT_CLASS (st_scroll_view_fade_parent_class)->dispose (gobject);
}
static void
st_scroll_view_fade_class_init (StScrollViewFadeClass *klass)
{
ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterOffscreenEffectClass *offscreen_class;
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
gobject_class->dispose = st_scroll_view_fade_dispose;
meta_class->set_actor = st_scroll_view_fade_set_actor;
effect_class->pre_paint = st_scroll_view_fade_pre_paint;
offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
offscreen_class->create_texture = st_scroll_view_fade_create_texture;
offscreen_class->paint_target = st_scroll_view_fade_paint_target;
}
static void
st_scroll_view_fade_init (StScrollViewFade *self)
{
static CoglHandle shader = COGL_INVALID_HANDLE;
if (shader == COGL_INVALID_HANDLE)
{
if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
{
shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
cogl_shader_source (shader, fade_glsl_shader);
cogl_shader_compile (shader);
if (!cogl_shader_is_compiled (shader))
{
gchar *log_buf = cogl_shader_get_info_log (shader);
g_warning (G_STRLOC ": Unable to compile the fade shader: %s",
log_buf);
g_free (log_buf);
cogl_handle_unref (shader);
shader = COGL_INVALID_HANDLE;
}
}
}
self->shader = shader;
self->is_attached = FALSE;
self->tex_uniform = -1;
self->height_uniform = -1;
self->width_uniform = -1;
self->scrollbar_width_uniform = -1;
self->offset_top_uniform = -1;
self->offset_bottom_uniform = -1;
if (shader != COGL_INVALID_HANDLE)
cogl_handle_ref (self->shader);
}
ClutterEffect *
st_scroll_view_fade_new (void)
{
return g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
}

View File

@ -0,0 +1,40 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scroll-view-fade.h: Edge fade effect for StScrollView
*
* Copyright 2010 Intel Corporation.
* Copyright 2011 Adel Gadllah
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ST_SCROLL_VIEW_FADE_H__
#define __ST_SCROLL_VIEW_FADE_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_SCROLL_VIEW_FADE (st_scroll_view_fade_get_type ())
#define ST_SCROLL_VIEW_FADE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFade))
#define ST_IS_SCROLL_VIEW_FADE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW_FADE))
typedef struct _StScrollViewFade StScrollViewFade;
GType st_scroll_view_fade_get_type (void) G_GNUC_CONST;
ClutterEffect *st_scroll_view_fade_new (void);
G_END_DECLS
#endif /* __ST_SCROLL_VIEW_FADE_H__ */

View File

@ -62,6 +62,7 @@
#include "st-marshal.h"
#include "st-scroll-bar.h"
#include "st-scrollable.h"
#include "st-scroll-view-fade.h"
#include <clutter/clutter.h>
#include <math.h>
@ -93,20 +94,17 @@ struct _StScrollViewPrivate
GtkPolicyType hscrollbar_policy;
GtkPolicyType vscrollbar_policy;
ClutterActor *top_shadow;
ClutterActor *bottom_shadow;
gfloat row_size;
gfloat column_size;
gboolean vshadows;
gboolean vfade;
StScrollViewFade *vfade_effect;
gboolean row_size_set : 1;
gboolean column_size_set : 1;
guint mouse_scroll : 1;
guint hscrollbar_visible : 1;
guint vscrollbar_visible : 1;
guint top_shadow_visible : 1;
guint bottom_shadow_visible : 1;
};
enum {
@ -117,7 +115,7 @@ enum {
PROP_HSCROLLBAR_POLICY,
PROP_VSCROLLBAR_POLICY,
PROP_MOUSE_SCROLL,
PROP_VSHADOWS
PROP_VFADE
};
static void
@ -145,80 +143,50 @@ st_scroll_view_get_property (GObject *object,
case PROP_MOUSE_SCROLL:
g_value_set_boolean (value, priv->mouse_scroll);
break;
case PROP_VSHADOWS:
g_value_set_boolean (value, priv->vshadows);
case PROP_VFADE:
g_value_set_boolean (value, priv->vfade);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
update_shadow_visibility (StScrollView *scroll)
{
StScrollViewPrivate *priv = scroll->priv;
if (priv->vshadows)
{
gdouble value, lower, upper, page_size;
st_adjustment_get_values (priv->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
priv->top_shadow_visible = value > lower + 0.1;
priv->bottom_shadow_visible = value < upper - page_size - 0.1;
}
else
{
priv->top_shadow_visible = FALSE;
priv->bottom_shadow_visible = FALSE;
}
}
/**
* st_scroll_view_set_vshadows:
* st_scroll_view_set_vfade:
* @self: a #StScrollView
* @vshadows: Whether to enable vertical shadows
* @vfade: Whether to enable the vertical fade effect
*
* Sets whether to show shadows at the top and bottom of the area. Shadows
* are omitted when fully scrolled to that edge.
* Sets whether to fade the content at the top and bottom of the area when not
* fully scrolled to that edge.
*/
void
st_scroll_view_set_vshadows (StScrollView *self,
gboolean vshadows)
st_scroll_view_set_vfade (StScrollView *self,
gboolean vfade)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
vshadows = vshadows != FALSE;
if (priv->vshadows == vshadows)
vfade = vfade != FALSE;
if (priv->vfade == vfade)
return;
priv->vshadows = vshadows;
priv->vfade = vfade;
if (vshadows)
if (vfade)
{
if (priv->top_shadow)
{
clutter_actor_show (priv->top_shadow);
clutter_actor_show (priv->bottom_shadow);
}
else
{
priv->top_shadow = g_object_new (ST_TYPE_BIN, "style-class", "top-shadow", NULL);
priv->bottom_shadow = g_object_new (ST_TYPE_BIN, "style-class", "bottom-shadow", NULL);
if (priv->vfade_effect == NULL)
priv->vfade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
clutter_actor_set_parent (priv->bottom_shadow, CLUTTER_ACTOR (self));
clutter_actor_set_parent (priv->top_shadow, CLUTTER_ACTOR (self));
}
clutter_actor_add_effect (CLUTTER_ACTOR (self), CLUTTER_EFFECT (priv->vfade_effect));
}
else
else
{
clutter_actor_hide (priv->top_shadow);
clutter_actor_hide (priv->bottom_shadow);
clutter_actor_remove_effect (CLUTTER_ACTOR (self), CLUTTER_EFFECT (priv->vfade_effect));
priv->vfade_effect = NULL;
}
update_shadow_visibility (self);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
g_object_notify (G_OBJECT (self), "vshadows");
g_object_notify (G_OBJECT (self), "vfade");
}
static void
@ -232,8 +200,8 @@ st_scroll_view_set_property (GObject *object,
switch (property_id)
{
case PROP_VSHADOWS:
st_scroll_view_set_vshadows (self, g_value_get_boolean (value));
case PROP_VFADE:
st_scroll_view_set_vfade (self, g_value_get_boolean (value));
break;
case PROP_MOUSE_SCROLL:
st_scroll_view_set_mouse_scrolling (self,
@ -259,6 +227,12 @@ st_scroll_view_dispose (GObject *object)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
if (priv->vfade_effect)
{
clutter_actor_remove_effect (CLUTTER_ACTOR (object), CLUTTER_EFFECT (priv->vfade_effect));
priv->vfade_effect = NULL;
}
if (priv->vscroll)
clutter_actor_destroy (priv->vscroll);
@ -284,20 +258,6 @@ st_scroll_view_dispose (GObject *object)
priv->vadjustment = NULL;
}
/* since it's impossible to get a handle to these actors, we can
* just directly unparent them and not go through destroy/remove */
if (priv->top_shadow)
{
clutter_actor_unparent (priv->top_shadow);
priv->top_shadow = NULL;
}
if (priv->bottom_shadow)
{
clutter_actor_unparent (priv->bottom_shadow);
priv->bottom_shadow = NULL;
}
G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
}
@ -314,11 +274,6 @@ st_scroll_view_paint (ClutterActor *actor)
clutter_actor_paint (priv->hscroll);
if (priv->vscrollbar_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
clutter_actor_paint (priv->vscroll);
if (priv->top_shadow_visible)
clutter_actor_paint (priv->top_shadow);
if (priv->bottom_shadow_visible)
clutter_actor_paint (priv->bottom_shadow);
}
static void
@ -532,18 +487,6 @@ st_scroll_view_get_preferred_height (ClutterActor *actor,
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static gfloat
get_shadow_height (ClutterActor *shadow)
{
gfloat natural_height;
/* The shadows are empty StBin and have no height-for-width behavior */
clutter_actor_get_preferred_height (shadow, -1, NULL, &natural_height);
return natural_height;
}
static void
st_scroll_view_allocate (ClutterActor *actor,
const ClutterActorBox *box,
@ -688,25 +631,6 @@ st_scroll_view_allocate (ClutterActor *actor,
if (priv->child)
clutter_actor_allocate (priv->child, &child_box, flags);
/* Shadows */
if (priv->top_shadow && CLUTTER_ACTOR_IS_VISIBLE (priv->top_shadow))
{
child_box.x1 = content_box.x1;
child_box.y1 = content_box.y1;
child_box.x2 = MAX (child_box.x1, content_box.x2 - sb_width);
child_box.y2 = content_box.y1 + get_shadow_height (priv->top_shadow);
clutter_actor_allocate (priv->top_shadow, &child_box, flags);
}
if (priv->bottom_shadow && CLUTTER_ACTOR_IS_VISIBLE (priv->bottom_shadow))
{
child_box.x1 = content_box.x1;
child_box.y1 = content_box.y2 - sb_height - get_shadow_height (priv->bottom_shadow);
child_box.x2 = MAX (content_box.x1, content_box.x2 - sb_width);
child_box.y2 = content_box.y2 - sb_height;
clutter_actor_allocate (priv->bottom_shadow, &child_box, flags);
}
priv->hscrollbar_visible = hscrollbar_visible;
priv->vscrollbar_visible = vscrollbar_visible;
}
@ -719,12 +643,6 @@ st_scroll_view_style_changed (StWidget *widget)
st_widget_style_changed (ST_WIDGET (priv->hscroll));
st_widget_style_changed (ST_WIDGET (priv->vscroll));
if (priv->top_shadow)
{
st_widget_style_changed (ST_WIDGET (priv->top_shadow));
st_widget_style_changed (ST_WIDGET (priv->bottom_shadow));
}
ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget);
}
@ -857,31 +775,16 @@ st_scroll_view_class_init (StScrollViewClass *klass)
PROP_MOUSE_SCROLL,
pspec);
pspec = g_param_spec_boolean ("vshadows",
pspec = g_param_spec_boolean ("vfade",
"Vertical Shadows",
"Show shadows at the top and and bottom of the area unless fully scrolled to that edge",
"Fade the content at the top and and bottom of the area unless fully scrolled to that edge",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_VSHADOWS,
PROP_VFADE,
pspec);
}
static void
child_adjustment_changed_cb (StAdjustment *adjustment,
StScrollView *scroll)
{
update_shadow_visibility (scroll);
}
static void
child_adjustment_notify_value (GObject *gobject,
GParamSpec *pspec,
StScrollView *scroll)
{
update_shadow_visibility (scroll);
}
static void
st_scroll_view_init (StScrollView *self)
{
@ -897,10 +800,6 @@ st_scroll_view_init (StScrollView *self)
NULL);
priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT, NULL);
g_signal_connect (priv->vadjustment, "changed",
G_CALLBACK (child_adjustment_changed_cb), self);
g_signal_connect (priv->vadjustment, "notify::value",
G_CALLBACK (child_adjustment_notify_value), self);
priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR,
"adjustment", priv->vadjustment,
"vertical", TRUE,
@ -988,12 +887,6 @@ st_scroll_view_foreach_with_internals (ClutterContainer *container,
if (priv->vscroll != NULL)
callback (priv->vscroll, user_data);
if (priv->top_shadow)
{
callback (priv->top_shadow, user_data);
callback (priv->bottom_shadow, user_data);
}
}
static void

View File

@ -84,8 +84,8 @@ void st_scroll_view_set_policy (StScrollView *scroll,
GtkPolicyType hscroll,
GtkPolicyType vscroll);
void st_scroll_view_set_vshadows (StScrollView *self,
gboolean vshadows);
void st_scroll_view_set_vfade (StScrollView *self,
gboolean vfade);
G_END_DECLS

View File

@ -315,12 +315,12 @@ st_table_calculate_col_widths (StTable *table,
for (list = children; list; list = list->next)
{
gint row, col;
gint col;
gfloat w_min, w_pref;
gboolean x_expand;
StTableChild *meta;
ClutterActor *child;
gint col_span, row_span;
gint col_span;
child = CLUTTER_ACTOR (list->data);
@ -331,10 +331,8 @@ st_table_calculate_col_widths (StTable *table,
/* get child properties */
col = meta->col;
row = meta->row;
x_expand = meta->x_expand;
col_span = meta->col_span;
row_span = meta->row_span;
if (x_expand)
is_expand_col[col] = TRUE;
@ -428,7 +426,7 @@ st_table_calculate_row_heights (StTable *table,
{
gint row, col, cell_width;
gfloat h_min, h_pref;
gboolean x_expand, y_expand;
gboolean y_expand;
StTableChild *meta;
ClutterActor *child;
gint col_span, row_span;
@ -443,7 +441,6 @@ st_table_calculate_row_heights (StTable *table,
/* get child properties */
col = meta->col;
row = meta->row;
x_expand = meta->x_expand;
y_expand = meta->y_expand;
col_span = meta->col_span;
row_span = meta->row_span;

View File

@ -30,6 +30,7 @@
#define CACHE_PREFIX_GICON "gicon:"
#define CACHE_PREFIX_URI "uri:"
#define CACHE_PREFIX_URI_FOR_CAIRO "uri-for-cairo:"
#define CACHE_PREFIX_THUMBNAIL_URI "thumbnail-uri:"
#define CACHE_PREFIX_RAW_CHECKSUM "raw-checksum:"
#define CACHE_PREFIX_COMPRESSED_CHECKSUM "compressed-checksum:"
@ -798,6 +799,27 @@ pixbuf_to_cogl_handle (GdkPixbuf *pixbuf)
gdk_pixbuf_get_pixels (pixbuf));
}
static cairo_surface_t *
pixbuf_to_cairo_surface (GdkPixbuf *pixbuf)
{
cairo_surface_t *dummy_surface;
cairo_pattern_t *pattern;
cairo_surface_t *surface;
cairo_t *cr;
dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
cr = cairo_create (dummy_surface);
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
pattern = cairo_get_source (cr);
cairo_pattern_get_surface (pattern, &surface);
cairo_surface_reference (surface);
cairo_destroy (cr);
cairo_surface_destroy (dummy_surface);
return surface;
}
static GdkPixbuf *
load_pixbuf_fallback(AsyncTextureLoadData *data)
{
@ -1504,6 +1526,45 @@ out:
return texdata;
}
static cairo_surface_t *
st_texture_cache_load_uri_sync_to_cairo_surface (StTextureCache *cache,
StTextureCachePolicy policy,
const gchar *uri,
int available_width,
int available_height,
GError **error)
{
cairo_surface_t *surface;
GdkPixbuf *pixbuf;
char *key;
key = g_strconcat (CACHE_PREFIX_URI_FOR_CAIRO, uri, NULL);
surface = g_hash_table_lookup (cache->priv->keyed_cache, key);
if (surface == NULL)
{
pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
if (!pixbuf)
goto out;
surface = pixbuf_to_cairo_surface (pixbuf);
g_object_unref (pixbuf);
if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
{
cairo_surface_reference (surface);
g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), surface);
}
}
else
cairo_surface_reference (surface);
out:
g_free (key);
return surface;
}
/**
* st_texture_cache_load_uri_sync:
*
@ -1582,6 +1643,43 @@ st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
return texture;
}
/**
* st_texture_cache_load_file_to_cairo_surface:
* @cache: A #StTextureCache
* @file_path: Path to a file in supported image format
*
* This function synchronously loads the given file path
* into a cairo surface. On error, a warning is emitted
* and %NULL is returned.
*
* Returns: (transfer full): a new #cairo_surface_t *
*/
cairo_surface_t *
st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
const gchar *file_path)
{
cairo_surface_t *surface;
GFile *file;
char *uri;
GError *error = NULL;
file = g_file_new_for_path (file_path);
uri = g_file_get_uri (file);
surface = st_texture_cache_load_uri_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
uri, -1, -1, &error);
g_object_unref (file);
g_free (uri);
if (surface == NULL)
{
g_warning ("Failed to load %s: %s", file_path, error->message);
g_clear_error (&error);
return NULL;
}
return surface;
}
/**
* st_texture_cache_load_file_simple:
* @cache: A #StTextureCache

View File

@ -119,6 +119,9 @@ ClutterActor *st_texture_cache_load_uri_sync (StTextureCache *cache,
CoglHandle st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
const gchar *file_path);
cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
const gchar *file_path);
ClutterActor *st_texture_cache_load_file_simple (StTextureCache *cache,
const gchar *file_path);

View File

@ -427,37 +427,37 @@ get_arbitrary_border_color (StThemeNode *node,
st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
}
static CoglHandle
st_theme_node_render_gradient (StThemeNode *node)
static gboolean
st_theme_node_has_visible_outline (StThemeNode *node)
{
if (node->background_color.alpha > 0)
return TRUE;
if (node->background_gradient_end.alpha > 0)
return TRUE;
if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
return TRUE;
if (node->border_width[ST_SIDE_TOP] > 0 ||
node->border_width[ST_SIDE_LEFT] > 0 ||
node->border_width[ST_SIDE_RIGHT] > 0 ||
node->border_width[ST_SIDE_BOTTOM] > 0)
return TRUE;
return FALSE;
}
static cairo_pattern_t *
create_cairo_pattern_of_background_gradient (StThemeNode *node)
{
CoglHandle texture;
int radius[4], i;
cairo_t *cr;
cairo_surface_t *surface;
cairo_pattern_t *pattern;
ClutterColor border_color;
int border_width[4];
guint rowstride;
guchar *data;
rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, node->alloc_width);
data = g_new0 (guchar, node->alloc_height * rowstride);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
node->alloc_width,
node->alloc_height,
rowstride);
cr = cairo_create (surface);
/* TODO - support non-uniform border colors */
get_arbitrary_border_color (node, &border_color);
for (i = 0; i < 4; i++)
{
border_width[i] = st_theme_node_get_border_width (node, i);
radius[i] = st_theme_node_get_border_radius (node, i);
}
g_return_val_if_fail (node->background_gradient_type != ST_GRADIENT_NONE,
NULL);
if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
@ -482,45 +482,374 @@ st_theme_node_render_gradient (StThemeNode *node)
node->background_gradient_end.green / 255.,
node->background_gradient_end.blue / 255.,
node->background_gradient_end.alpha / 255.);
return pattern;
}
static cairo_pattern_t *
create_cairo_pattern_of_background_image (StThemeNode *node,
gboolean *needs_background_fill)
{
cairo_surface_t *surface;
cairo_pattern_t *pattern;
cairo_content_t content;
cairo_matrix_t matrix;
const char *file;
double height_ratio, width_ratio;
int file_width;
int file_height;
StTextureCache *texture_cache;
file = st_theme_node_get_background_image (node);
texture_cache = st_texture_cache_get_default ();
surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file);
if (surface == NULL)
return NULL;
g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
content = cairo_surface_get_content (surface);
pattern = cairo_pattern_create_for_surface (surface);
file_width = cairo_image_surface_get_width (surface);
file_height = cairo_image_surface_get_height (surface);
height_ratio = file_height / node->alloc_height;
width_ratio = file_width / node->alloc_width;
*needs_background_fill = TRUE;
if ((file_width > node->alloc_width || file_height > node->alloc_height)
&& !node->background_position_set)
{
double scale_factor;
double x_offset, y_offset;
if (width_ratio > height_ratio)
{
double scaled_height;
/* center vertically */
scale_factor = width_ratio;
scaled_height = file_height / scale_factor;
x_offset = 0.;
y_offset = - (node->alloc_height / 2. - scaled_height / 2.);
}
else
{
double scaled_width;
/* center horizontally */
scale_factor = height_ratio;
scaled_width = file_width / scale_factor;
y_offset = 0.;
x_offset = - (node->alloc_width / 2. - scaled_width / 2.);
}
cairo_matrix_init_translate (&matrix, x_offset, y_offset);
cairo_matrix_scale (&matrix, scale_factor, scale_factor);
cairo_pattern_set_matrix (pattern, &matrix);
/* If it's opaque, and when scaled, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA && width_ratio == height_ratio)
*needs_background_fill = FALSE;
}
else
{
double x_offset, y_offset;
if (node->background_position_set)
{
x_offset = -node->background_position_x;
y_offset = -node->background_position_y;
}
else
{
if (node->alloc_width > file_width)
x_offset = - (node->alloc_width / 2.0 - file_width / 2.0);
else
x_offset = - (file_width / 2.0 - node->alloc_width / 2.0);
if (node->alloc_height > file_height)
y_offset = - (node->alloc_height / 2.0 - file_height / 2.0);
else
y_offset = - (file_height / 2.0 - node->alloc_height / 2.0);
}
/* If it's opaque, and when translated, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA
&& -x_offset <= 0
&& -x_offset + file_width >= node->alloc_width
&& -y_offset <= 0
&& -y_offset + file_height >= node->alloc_height)
*needs_background_fill = FALSE;
cairo_matrix_init_translate (&matrix, x_offset, y_offset);
cairo_pattern_set_matrix (pattern, &matrix);
}
return pattern;
}
static void
paint_background_image_shadow_to_cairo_context (StThemeNode *node,
StShadow *shadow_spec,
cairo_pattern_t *pattern,
cairo_t *cr,
cairo_path_t *interior_path,
cairo_path_t *outline_path,
int x,
int y,
int width,
int height)
{
cairo_pattern_t *shadow_pattern;
g_assert (shadow_spec != NULL);
g_assert (pattern != NULL);
if (outline_path != NULL)
{
cairo_surface_t *clipped_surface;
cairo_pattern_t *clipped_pattern;
cairo_t *temp_cr;
/* Prerender the pattern to a temporary surface,
* so it's properly clipped before we create a shadow from it
*/
clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
temp_cr = cairo_create (clipped_surface);
cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (temp_cr);
cairo_set_operator (temp_cr, CAIRO_OPERATOR_SOURCE);
if (interior_path != NULL)
{
cairo_append_path (temp_cr, interior_path);
cairo_clip (temp_cr);
}
cairo_append_path (temp_cr, outline_path);
cairo_translate (temp_cr, x, y);
cairo_set_source (temp_cr, pattern);
cairo_clip (temp_cr);
cairo_paint (temp_cr);
cairo_destroy (temp_cr);
clipped_pattern = cairo_pattern_create_for_surface (clipped_surface);
cairo_surface_destroy (clipped_surface);
shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
clipped_pattern);
cairo_pattern_destroy (clipped_pattern);
}
else
{
shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
pattern);
}
/* Stamp the shadow pattern out in the appropriate color
* in a new layer
*/
cairo_push_group (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba (cr,
shadow_spec->color.red / 255.0,
shadow_spec->color.green / 255.0,
shadow_spec->color.blue / 255.0,
shadow_spec->color.alpha / 255.0);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
cairo_set_source (cr, shadow_pattern);
cairo_paint (cr);
cairo_pattern_destroy (shadow_pattern);
cairo_pop_group_to_source (cr);
/* mask and merge the shadow
*/
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_save (cr);
if (interior_path != NULL)
{
/* If there are borders, clip the shadow to the interior
* of the borders
*/
cairo_append_path (cr, interior_path);
cairo_clip (cr);
}
else if (outline_path != NULL)
{
/* If there is a visible outline, clip the shadow to
* that outline
*/
cairo_append_path (cr, outline_path);
cairo_clip (cr);
}
cairo_paint (cr);
cairo_restore (cr);
}
/* In order for borders to be smoothly blended with non-solid backgrounds,
* we need to use cairo. This function is a slow fallback path for those
* cases (gradients, background images, etc).
*/
static CoglHandle
st_theme_node_render_background_with_border (StThemeNode *node)
{
StBorderImage *border_image;
CoglHandle texture;
int radius[4], i;
cairo_t *cr;
cairo_surface_t *surface;
StShadow *shadow_spec;
cairo_pattern_t *pattern = NULL;
cairo_path_t *outline_path = NULL;
gboolean draw_solid_background = TRUE;
gboolean draw_background_image_shadow = FALSE;
gboolean has_visible_outline;
ClutterColor border_color;
int border_width[4];
guint rowstride;
guchar *data;
ClutterActorBox actor_box;
ClutterActorBox paint_box;
cairo_path_t *interior_path = NULL;
border_image = st_theme_node_get_border_image (node);
shadow_spec = st_theme_node_get_background_image_shadow (node);
actor_box.x1 = 0;
actor_box.x2 = node->alloc_width;
actor_box.y1 = 0;
actor_box.y2 = node->alloc_height;
/* If there's a background image shadow, we
* may need to create an image bigger than the nodes
* allocation
*/
st_theme_node_get_paint_box (node, &actor_box, &paint_box);
/* translate the boxes so the paint box is at 0,0
*/
actor_box.x1 += - paint_box.x1;
actor_box.x2 += - paint_box.x1;
actor_box.y1 += - paint_box.y1;
actor_box.y2 += - paint_box.y1;
paint_box.x2 += - paint_box.x1;
paint_box.x1 += - paint_box.x1;
paint_box.y2 += - paint_box.y1;
paint_box.y1 += - paint_box.y1;
rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
paint_box.x2 - paint_box.x1);
data = g_new0 (guchar, (paint_box.y2 - paint_box.y1) * rowstride);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1,
rowstride);
cr = cairo_create (surface);
/* TODO - support non-uniform border colors */
get_arbitrary_border_color (node, &border_color);
for (i = 0; i < 4; i++)
{
border_width[i] = st_theme_node_get_border_width (node, i);
radius[i] = st_theme_node_get_border_radius (node, i);
}
/* Note we don't support translucent background images on top
* of gradients. It's strictly either/or.
*/
if (node->background_gradient_type != ST_GRADIENT_NONE)
{
pattern = create_cairo_pattern_of_background_gradient (node);
draw_solid_background = FALSE;
}
else
{
const char *background_image;
background_image = st_theme_node_get_background_image (node);
if (background_image != NULL)
{
pattern = create_cairo_pattern_of_background_image (node,
&draw_solid_background);
if (shadow_spec && pattern != NULL)
draw_background_image_shadow = TRUE;
}
}
if (pattern == NULL)
draw_solid_background = TRUE;
has_visible_outline = st_theme_node_has_visible_outline (node);
/* Create a path for the background's outline first */
if (radius[ST_CORNER_TOPLEFT] > 0)
cairo_arc (cr,
radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT],
actor_box.x1 + radius[ST_CORNER_TOPLEFT],
actor_box.y1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
else
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, node->alloc_width - radius[ST_CORNER_TOPRIGHT], 0);
cairo_move_to (cr, actor_box.x1, actor_box.y1);
cairo_line_to (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1);
if (radius[ST_CORNER_TOPRIGHT] > 0)
cairo_arc (cr,
node->alloc_width - radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT],
actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
actor_box.x1 + radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]);
cairo_line_to (cr, actor_box.x2, actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT]);
if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
cairo_arc (cr,
node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height);
cairo_line_to (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2);
if (radius[ST_CORNER_BOTTOMLEFT] > 0)
cairo_arc (cr,
radius[ST_CORNER_BOTTOMLEFT],
node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
cairo_close_path (cr);
outline_path = cairo_copy_path (cr);
/* If we have a border, we fill the outline with the border
* color and create the inline shape for the background gradient;
/* If we have a solid border, we fill the outline shape with the border
* color and create the inline shape for the background;
* otherwise the outline shape is filled with the background
* gradient directly
* directly
*/
if (border_width[ST_SIDE_TOP] > 0 ||
border_width[ST_SIDE_RIGHT] > 0 ||
border_width[ST_SIDE_BOTTOM] > 0 ||
border_width[ST_SIDE_LEFT] > 0)
if (border_image == NULL &&
(border_width[ST_SIDE_TOP] > 0 ||
border_width[ST_SIDE_RIGHT] > 0 ||
border_width[ST_SIDE_BOTTOM] > 0 ||
border_width[ST_SIDE_LEFT] > 0))
{
cairo_set_source_rgba (cr,
border_color.red / 255.,
@ -532,76 +861,124 @@ st_theme_node_render_gradient (StThemeNode *node)
if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
border_width[ST_SIDE_LEFT]))
elliptical_arc (cr,
radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT],
actor_box.x1 + radius[ST_CORNER_TOPLEFT],
actor_box.y1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
M_PI, 3 * M_PI / 2);
else
cairo_move_to (cr,
border_width[ST_SIDE_LEFT],
border_width[ST_SIDE_TOP]);
actor_box.x1 + border_width[ST_SIDE_LEFT],
actor_box.y1 + border_width[ST_SIDE_TOP]);
cairo_line_to (cr,
node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
border_width[ST_SIDE_TOP]);
actor_box.x2 - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
actor_box.y1 + border_width[ST_SIDE_TOP]);
if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
border_width[ST_SIDE_RIGHT]))
elliptical_arc (cr,
node->alloc_width - radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT],
actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
actor_box.y1 + radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
3 * M_PI / 2, 2 * M_PI);
else
cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT],
border_width[ST_SIDE_TOP]);
actor_box.x2 - border_width[ST_SIDE_RIGHT],
actor_box.y1 + border_width[ST_SIDE_TOP]);
cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT],
node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
actor_box.x2 - border_width[ST_SIDE_RIGHT],
actor_box.y2 - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
border_width[ST_SIDE_RIGHT]))
elliptical_arc (cr,
node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
0, M_PI / 2);
else
cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT],
node->alloc_height - border_width[ST_SIDE_BOTTOM]);
actor_box.x2 - border_width[ST_SIDE_RIGHT],
actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
cairo_line_to (cr,
MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
node->alloc_height - border_width[ST_SIDE_BOTTOM]);
actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
border_width[ST_SIDE_LEFT]))
elliptical_arc (cr,
radius[ST_CORNER_BOTTOMLEFT],
node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
M_PI / 2, M_PI);
else
cairo_line_to (cr,
border_width[ST_SIDE_LEFT],
node->alloc_height - border_width[ST_SIDE_BOTTOM]);
actor_box.x1 + border_width[ST_SIDE_LEFT],
actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
cairo_close_path (cr);
interior_path = cairo_copy_path (cr);
/* clip drawing to the region inside of the borders
*/
cairo_clip (cr);
/* But fill the pattern as if it started at the edge of outline,
* behind the borders. This is similar to
* background-clip: border-box; semantics.
*/
cairo_append_path (cr, outline_path);
}
cairo_set_source (cr, pattern);
cairo_fill (cr);
if (draw_solid_background)
{
cairo_set_source_rgba (cr,
node->background_color.red / 255.,
node->background_color.green / 255.,
node->background_color.blue / 255.,
node->background_color.alpha / 255.);
cairo_fill_preserve (cr);
}
cairo_pattern_destroy (pattern);
if (draw_background_image_shadow)
{
paint_background_image_shadow_to_cairo_context (node,
shadow_spec,
pattern,
cr,
interior_path,
has_visible_outline? outline_path : NULL,
actor_box.x1,
actor_box.y1,
paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1);
cairo_append_path (cr, outline_path);
}
texture = cogl_texture_new_from_data (node->alloc_width, node->alloc_height,
cairo_translate (cr, actor_box.x1, actor_box.y1);
if (pattern != NULL)
{
cairo_set_source (cr, pattern);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
}
if (outline_path != NULL)
cairo_path_destroy (outline_path);
if (interior_path != NULL)
cairo_path_destroy (interior_path);
texture = cogl_texture_new_from_data (paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1,
COGL_TEXTURE_NONE,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE,
@ -633,12 +1010,16 @@ _st_theme_node_free_drawing_state (StThemeNode *node)
cogl_handle_unref (node->background_material);
if (node->background_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->background_shadow_material);
if (node->border_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (node->border_texture);
if (node->border_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->border_material);
if (node->border_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->border_shadow_material);
if (node->border_slices_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (node->border_slices_texture);
if (node->border_slices_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->border_slices_material);
if (node->prerendered_texture != COGL_INVALID_HANDLE)
cogl_handle_unref (node->prerendered_texture);
if (node->prerendered_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->prerendered_material);
if (node->box_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (node->box_shadow_material);
for (corner_id = 0; corner_id < 4; corner_id++)
if (node->corner_material[corner_id] != COGL_INVALID_HANDLE)
@ -655,9 +1036,11 @@ _st_theme_node_init_drawing_state (StThemeNode *node)
node->background_texture = COGL_INVALID_HANDLE;
node->background_material = COGL_INVALID_HANDLE;
node->background_shadow_material = COGL_INVALID_HANDLE;
node->border_shadow_material = COGL_INVALID_HANDLE;
node->border_texture = COGL_INVALID_HANDLE;
node->border_material = COGL_INVALID_HANDLE;
node->box_shadow_material = COGL_INVALID_HANDLE;
node->border_slices_texture = COGL_INVALID_HANDLE;
node->border_slices_material = COGL_INVALID_HANDLE;
node->prerendered_texture = COGL_INVALID_HANDLE;
node->prerendered_material = COGL_INVALID_HANDLE;
for (corner_id = 0; corner_id < 4; corner_id++)
node->corner_material[corner_id] = COGL_INVALID_HANDLE;
@ -674,7 +1057,10 @@ st_theme_node_render_resources (StThemeNode *node,
{
StTextureCache *texture_cache;
StBorderImage *border_image;
StShadow *shadow_spec;
gboolean has_border;
gboolean has_border_radius;
StShadow *box_shadow_spec;
StShadow *background_image_shadow_spec;
const char *background_image;
texture_cache = st_texture_cache_get_default ();
@ -691,39 +1077,68 @@ st_theme_node_render_resources (StThemeNode *node,
_st_theme_node_ensure_background (node);
_st_theme_node_ensure_geometry (node);
shadow_spec = st_theme_node_get_shadow (node);
box_shadow_spec = st_theme_node_get_box_shadow (node);
if (node->border_width[ST_SIDE_TOP] > 0 ||
node->border_width[ST_SIDE_LEFT] > 0 ||
node->border_width[ST_SIDE_RIGHT] > 0 ||
node->border_width[ST_SIDE_BOTTOM] > 0)
has_border = TRUE;
else
has_border = FALSE;
if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
has_border_radius = TRUE;
else
has_border_radius = FALSE;
/* Load referenced images from disk and draw anything we need with cairo now */
background_image = st_theme_node_get_background_image (node);
border_image = st_theme_node_get_border_image (node);
if (border_image)
{
const char *filename;
filename = st_border_image_get_filename (border_image);
node->border_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
}
else if (node->background_gradient_type != ST_GRADIENT_NONE)
{
node->border_texture = st_theme_node_render_gradient (node);
node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
}
if (node->border_texture)
node->border_material = _st_create_texture_material (node->border_texture);
if (node->border_slices_texture)
node->border_slices_material = _st_create_texture_material (node->border_slices_texture);
else
node->border_material = COGL_INVALID_HANDLE;
node->border_slices_material = COGL_INVALID_HANDLE;
if (shadow_spec)
/* Use cairo to prerender the node if there is a gradient, or
* background image with borders and/or rounded corners,
* since we can't do those things easily with cogl.
*
* FIXME: if we could figure out ahead of time that a
* background image won't overlap with the node borders,
* then we could use cogl for that case.
*/
if ((node->background_gradient_type != ST_GRADIENT_NONE)
|| (background_image && (has_border || has_border_radius)))
node->prerendered_texture = st_theme_node_render_background_with_border (node);
if (node->prerendered_texture)
node->prerendered_material = _st_create_texture_material (node->prerendered_texture);
else
node->prerendered_material = COGL_INVALID_HANDLE;
if (box_shadow_spec)
{
if (node->border_texture != COGL_INVALID_HANDLE)
node->border_shadow_material = _st_create_shadow_material (shadow_spec,
node->border_texture);
else if (node->background_color.alpha > 0 ||
node->border_width[ST_SIDE_TOP] > 0 ||
node->border_width[ST_SIDE_LEFT] > 0 ||
node->border_width[ST_SIDE_RIGHT] > 0 ||
node->border_width[ST_SIDE_BOTTOM] > 0)
if (node->border_slices_texture != COGL_INVALID_HANDLE)
node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
node->border_slices_texture);
else if (node->prerendered_texture != COGL_INVALID_HANDLE)
node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
node->prerendered_texture);
else if (node->background_color.alpha > 0 || has_border)
{
CoglHandle buffer, offscreen;
@ -743,23 +1158,50 @@ st_theme_node_render_resources (StThemeNode *node,
cogl_pop_framebuffer ();
cogl_handle_unref (offscreen);
node->border_shadow_material = _st_create_shadow_material (shadow_spec,
buffer);
node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
buffer);
}
cogl_handle_unref (buffer);
}
}
background_image = st_theme_node_get_background_image (node);
if (background_image != NULL)
background_image_shadow_spec = st_theme_node_get_background_image_shadow (node);
if (background_image != NULL && !has_border && !has_border_radius)
{
CoglHandle texture;
texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image);
/* If no background position is specified, then we will automatically scale
* the background to fit within the node allocation. But, if a background
* position is specified, we won't scale the background, and it could
* potentially leak out of bounds. To prevent that, we subtexture from the
* in bounds area when necessary.
*/
if (node->background_position_set &&
(cogl_texture_get_width (texture) > width ||
cogl_texture_get_height (texture) > height))
{
CoglHandle subtexture;
subtexture = cogl_texture_new_from_sub_texture (texture,
0, 0,
width - node->background_position_x,
height - node->background_position_y);
cogl_handle_unref (texture);
node->background_texture = subtexture;
}
else
{
node->background_texture = texture;
}
node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image);
node->background_material = _st_create_texture_material (node->background_texture);
if (shadow_spec)
if (background_image_shadow_spec)
{
node->background_shadow_material = _st_create_shadow_material (shadow_spec,
node->background_shadow_material = _st_create_shadow_material (background_image_shadow_spec,
node->background_texture);
}
}
@ -1069,8 +1511,8 @@ st_theme_node_paint_sliced_border_image (StThemeNode *node,
st_border_image_get_borders (border_image,
&border_left, &border_right, &border_top, &border_bottom);
img_width = cogl_texture_get_width (node->border_texture);
img_height = cogl_texture_get_height (node->border_texture);
img_width = cogl_texture_get_width (node->border_slices_texture);
img_height = cogl_texture_get_height (node->border_slices_texture);
tx1 = border_left / img_width;
tx2 = (img_width - border_right) / img_width;
@ -1085,7 +1527,7 @@ st_theme_node_paint_sliced_border_image (StThemeNode *node,
if (ey < 0)
ey = border_bottom; /* FIXME ? */
material = node->border_material;
material = node->border_slices_material;
cogl_material_set_color4ub (material,
paint_opacity, paint_opacity, paint_opacity, paint_opacity);
@ -1230,41 +1672,58 @@ st_theme_node_paint (StThemeNode *node,
* - The combination of border image and a non-zero border radius is
* not supported; the background color will be drawn with square
* corners.
* - The combination of border image and a background gradient is not
* supported; the background will be drawn as a solid color
* - The background image is drawn above the border color or image,
* not below it.
* - We don't clip the background image to the (rounded) border area.
*
* The first three allow us to always draw with no more than a single
* border_image and a single background image above it.
* - The background image is drawn above the border color, not below it.
* - We clip the background image to the inside edges of the border
* instead of the outside edges of the border (but position the image
* such that it's aligned to the outside edges)
*/
if (node->border_shadow_material)
_st_paint_shadow_with_opacity (node->shadow,
node->border_shadow_material,
if (node->box_shadow_material)
_st_paint_shadow_with_opacity (node->box_shadow,
node->box_shadow_material,
&allocation,
paint_opacity);
if (node->border_material != COGL_INVALID_HANDLE)
if (node->prerendered_material != COGL_INVALID_HANDLE ||
node->border_slices_material != COGL_INVALID_HANDLE)
{
/* Gradients and border images are mutually exclusive at this time */
if (node->background_gradient_type != ST_GRADIENT_NONE)
paint_material_with_opacity (node->border_material, &allocation, paint_opacity);
else
if (node->prerendered_material != COGL_INVALID_HANDLE)
{
ClutterActorBox paint_box;
st_theme_node_get_paint_box (node, &allocation, &paint_box);
paint_material_with_opacity (node->prerendered_material,
&paint_box,
paint_opacity);
}
if (node->border_slices_material != COGL_INVALID_HANDLE)
st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity);
}
else
st_theme_node_paint_borders (node, box, paint_opacity);
{
st_theme_node_paint_borders (node, box, paint_opacity);
}
st_theme_node_paint_outline (node, box, paint_opacity);
if (node->background_texture != COGL_INVALID_HANDLE)
{
ClutterActorBox background_box;
gboolean has_visible_outline;
/* If the background doesn't have a border or opaque background,
* then we let its background image shadows leak out, but other
* wise we clip it.
*/
has_visible_outline = st_theme_node_has_visible_outline (node);
get_background_position (node, &allocation, &background_box);
if (has_visible_outline)
cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2);
/* CSS based drop shadows
*
* Drop shadows in ST are modelled after the CSS3 box-shadow property;
@ -1274,16 +1733,20 @@ st_theme_node_paint (StThemeNode *node,
* multiple shadows and allow for a more liberal placement of the color
* parameter - its interpretation defers significantly in that the shadow's
* shape is not determined by the bounding box, but by the CSS background
* image (we could exend this in the future to take other CSS properties
* like boder and background color into account).
* image. The drop shadows are allowed to escape the nodes allocation if
* there is nothing (like a border, or the edge of the background color)
* to logically confine it.
*/
if (node->background_shadow_material != COGL_INVALID_HANDLE)
_st_paint_shadow_with_opacity (node->shadow,
_st_paint_shadow_with_opacity (node->background_image_shadow,
node->background_shadow_material,
&background_box,
paint_opacity);
paint_material_with_opacity (node->background_material, &background_box, paint_opacity);
if (has_visible_outline)
cogl_clip_pop ();
}
}
@ -1316,16 +1779,20 @@ st_theme_node_copy_cached_paint_state (StThemeNode *node,
if (other->background_shadow_material)
node->background_shadow_material = cogl_handle_ref (other->background_shadow_material);
if (other->border_shadow_material)
node->border_shadow_material = cogl_handle_ref (other->border_shadow_material);
if (other->box_shadow_material)
node->box_shadow_material = cogl_handle_ref (other->box_shadow_material);
if (other->background_texture)
node->background_texture = cogl_handle_ref (other->background_texture);
if (other->background_material)
node->background_material = cogl_handle_ref (other->background_material);
if (other->border_texture)
node->border_texture = cogl_handle_ref (other->border_texture);
if (other->border_material)
node->border_material = cogl_handle_ref (other->border_material);
if (other->border_slices_texture)
node->border_slices_texture = cogl_handle_ref (other->border_slices_texture);
if (other->border_slices_material)
node->border_slices_material = cogl_handle_ref (other->border_slices_material);
if (other->prerendered_texture)
node->prerendered_texture = cogl_handle_ref (other->prerendered_texture);
if (other->prerendered_material)
node->prerendered_material = cogl_handle_ref (other->prerendered_material);
for (corner_id = 0; corner_id < 4; corner_id++)
if (other->corner_material[corner_id])
node->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]);

View File

@ -65,7 +65,8 @@ struct _StThemeNode {
char *background_image;
StBorderImage *border_image;
StShadow *shadow;
StShadow *box_shadow;
StShadow *background_image_shadow;
StShadow *text_shadow;
StIconColors *icon_colors;
@ -86,7 +87,8 @@ struct _StThemeNode {
guint background_computed : 1;
guint foreground_computed : 1;
guint border_image_computed : 1;
guint shadow_computed : 1;
guint box_shadow_computed : 1;
guint background_image_shadow_computed : 1;
guint text_shadow_computed : 1;
guint link_type : 2;
@ -95,11 +97,13 @@ struct _StThemeNode {
float alloc_height;
CoglHandle background_shadow_material;
CoglHandle border_shadow_material;
CoglHandle box_shadow_material;
CoglHandle background_texture;
CoglHandle background_material;
CoglHandle border_texture;
CoglHandle border_material;
CoglHandle border_slices_texture;
CoglHandle border_slices_material;
CoglHandle prerendered_texture;
CoglHandle prerendered_material;
CoglHandle corner_material[4];
};

View File

@ -182,6 +182,22 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
static void
calculate_offscreen_box (StThemeNodeTransition *transition,
const ClutterActorBox *allocation)
{
ClutterActorBox paint_box;
st_theme_node_transition_get_paint_box (transition,
allocation,
&paint_box);
transition->priv->offscreen_box.x1 = paint_box.x1 - allocation->x1;
transition->priv->offscreen_box.y1 = paint_box.y1 - allocation->y1;
transition->priv->offscreen_box.x2 = paint_box.x2 - allocation->x1;
transition->priv->offscreen_box.y2 = paint_box.y2 - allocation->y1;
}
void
st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
const ClutterActorBox *allocation,
ClutterActorBox *paint_box)
{
StThemeNodeTransitionPrivate *priv = transition->priv;
ClutterActorBox old_node_box, new_node_box;
@ -194,14 +210,10 @@ calculate_offscreen_box (StThemeNodeTransition *transition,
allocation,
&new_node_box);
priv->offscreen_box.x1 = MIN (old_node_box.x1, new_node_box.x1)
- allocation->x1;
priv->offscreen_box.y1 = MIN (old_node_box.y1, new_node_box.y1)
- allocation->y1;
priv->offscreen_box.x2 = MAX (old_node_box.x2, new_node_box.x2)
- allocation->x1;
priv->offscreen_box.y2 = MAX (old_node_box.y2, new_node_box.y2)
- allocation->y1;
paint_box->x1 = MIN (old_node_box.x1, new_node_box.x1);
paint_box->y1 = MIN (old_node_box.y1, new_node_box.y1);
paint_box->x2 = MAX (old_node_box.x2, new_node_box.x2);
paint_box->y2 = MAX (old_node_box.y2, new_node_box.y2);
}
static gboolean

View File

@ -65,6 +65,10 @@ void st_theme_node_transition_paint (StThemeNodeTransition *transition,
ClutterActorBox *allocation,
guint8 paint_opacity);
void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
const ClutterActorBox *allocation,
ClutterActorBox *paint_box);
G_END_DECLS
#endif

View File

@ -122,10 +122,16 @@ st_theme_node_finalize (GObject *object)
node->font_desc = NULL;
}
if (node->shadow)
if (node->box_shadow)
{
st_shadow_unref (node->shadow);
node->shadow = NULL;
st_shadow_unref (node->box_shadow);
node->box_shadow = NULL;
}
if (node->background_image_shadow)
{
st_shadow_unref (node->background_image_shadow);
node->background_image_shadow = NULL;
}
if (node->text_shadow)
@ -2606,16 +2612,17 @@ st_theme_node_get_vertical_padding (StThemeNode *node)
return padding;
}
static void
parse_shadow_property (StThemeNode *node,
CRDeclaration *decl,
ClutterColor *color,
gdouble *xoffset,
gdouble *yoffset,
gdouble *blur,
gdouble *spread)
static GetFromTermResult
parse_shadow_property (StThemeNode *node,
CRDeclaration *decl,
ClutterColor *color,
gdouble *xoffset,
gdouble *yoffset,
gdouble *blur,
gdouble *spread)
{
/* Set value for width/color/blur in any order */
GetFromTermResult result;
CRTerm *term;
int n_offsets = 0;
@ -2628,8 +2635,6 @@ parse_shadow_property (StThemeNode *node,
for (term = decl->value; term; term = term->next)
{
GetFromTermResult result;
if (term->type == TERM_NUMBER)
{
gdouble value;
@ -2637,7 +2642,16 @@ parse_shadow_property (StThemeNode *node,
multiplier = (term->unary_op == MINUS_UOP) ? -1. : 1.;
result = get_length_from_term (node, term, FALSE, &value);
if (result != VALUE_NOT_FOUND)
if (result == VALUE_INHERIT)
{
/* we only allow inherit on the line by itself */
if (n_offsets > 0)
return VALUE_NOT_FOUND;
else
return VALUE_INHERIT;
}
else if (result == VALUE_FOUND)
{
switch (n_offsets++)
{
@ -2665,57 +2679,200 @@ parse_shadow_property (StThemeNode *node,
}
result = get_color_from_term (node, term, color);
if (result != VALUE_NOT_FOUND)
if (result == VALUE_INHERIT)
{
if (n_offsets > 0)
return VALUE_NOT_FOUND;
else
return VALUE_INHERIT;
}
else if (result == VALUE_FOUND)
{
continue;
}
}
/* The only required terms are the x and y offsets
*/
if (n_offsets >= 2)
return VALUE_FOUND;
else
return VALUE_NOT_FOUND;
}
/**
* st_theme_node_get_shadow:
* st_theme_node_lookup_shadow:
* @node: a #StThemeNode
* @property_name: The name of the shadow property
* @inherit: if %TRUE, if a value is not found for the property on the
* node, then it will be looked up on the parent node, and then on the
* parent's parent, and so forth. Note that if the property has a
* value of 'inherit' it will be inherited even if %FALSE is passed
* in for @inherit; this only affects the default behavior for inheritance.
* @shadow: (out): location to store the shadow
*
* Gets the value for the -st-shadow style property
* If the property is not found, the value in the shadow variable will not
* be changed.
*
* Return value: (transfer none): the node's shadow, or %NULL
* if node has no shadow
* Generically looks up a property containing a set of shadow values. When
* specific getters (like st_theme_node_get_box_shadow ()) exist, they
* should be used instead. They are cached, so more efficient, and have
* handling for shortcut properties and other details of CSS.
*
* See also st_theme_node_get_shadow(), which provides a simpler API.
*
* Return value: %TRUE if the property was found in the properties for this
* theme node (or in the properties of parent nodes when inheriting.)
*/
StShadow *
st_theme_node_get_shadow (StThemeNode *node)
gboolean
st_theme_node_lookup_shadow (StThemeNode *node,
const char *property_name,
gboolean inherit,
StShadow **shadow)
{
ClutterColor color = { 0., };
gdouble xoffset = 0.;
gdouble yoffset = 0.;
gdouble blur = 0.;
gdouble spread = 0.;
int i;
if (node->shadow_computed)
return node->shadow;
node->shadow = NULL;
node->shadow_computed = TRUE;
ensure_properties (node);
for (i = node->n_properties - 1; i >= 0; i--)
{
CRDeclaration *decl = node->properties[i];
if (strcmp (decl->property->stryng->str, "-st-shadow") == 0)
if (strcmp (decl->property->stryng->str, property_name) == 0)
{
ClutterColor color;
gdouble xoffset;
gdouble yoffset;
gdouble blur;
gdouble spread;
parse_shadow_property (node, decl,
&color, &xoffset, &yoffset, &blur, &spread);
node->shadow = st_shadow_new (&color,
xoffset, yoffset,
blur, spread);
return node->shadow;
GetFromTermResult result = parse_shadow_property (node,
decl,
&color,
&xoffset,
&yoffset,
&blur,
&spread);
if (result == VALUE_FOUND)
{
*shadow = st_shadow_new (&color, xoffset, yoffset, blur, spread);
return TRUE;
}
else if (result == VALUE_INHERIT)
{
if (node->parent_node)
return st_theme_node_lookup_shadow (node->parent_node,
property_name,
inherit,
shadow);
else
break;
}
}
}
if (inherit && node->parent_node)
return st_theme_node_lookup_shadow (node->parent_node,
property_name,
inherit,
shadow);
return FALSE;
}
/**
* st_theme_node_get_shadow:
* @node: a #StThemeNode
* @property_name: The name of the shadow property
*
* Generically looks up a property containing a set of shadow values. When
* specific getters (like st_theme_node_get_box_shadow()) exist, they
* should be used instead. They are cached, so more efficient, and have
* handling for shortcut properties and other details of CSS.
*
* Like st_theme_get_length(), this does not print a warning if the property is
* not found; it just returns %NULL
*
* See also st_theme_node_lookup_shadow (), which provides more options.
*
* Return value: (transfer full): the shadow, or %NULL if the property was not found.
*/
StShadow *
st_theme_node_get_shadow (StThemeNode *node,
const char *property_name)
{
StShadow *shadow;
if (st_theme_node_lookup_shadow (node, property_name, FALSE, &shadow))
return shadow;
else
return NULL;
}
/**
* st_theme_node_get_box_shadow:
* @node: a #StThemeNode
*
* Gets the value for the box-shadow style property
*
* Return value: (transfer none): the node's shadow, or %NULL
* if node has no shadow
*/
StShadow *
st_theme_node_get_box_shadow (StThemeNode *node)
{
StShadow *shadow;
if (node->box_shadow_computed)
return node->box_shadow;
node->box_shadow = NULL;
node->box_shadow_computed = TRUE;
if (st_theme_node_lookup_shadow (node,
"box-shadow",
FALSE,
&shadow))
{
node->box_shadow = shadow;
return node->box_shadow;
}
return NULL;
}
/**
* st_theme_node_get_background_image_shadow:
* @node: a #StThemeNode
*
* Gets the value for the -st-background-image-shadow style property
*
* Return value: (transfer none): the node's background image shadow, or %NULL
* if node has no such shadow
*/
StShadow *
st_theme_node_get_background_image_shadow (StThemeNode *node)
{
StShadow *shadow;
if (node->background_image_shadow_computed)
return node->background_image_shadow;
node->background_image_shadow = NULL;
node->background_image_shadow_computed = TRUE;
if (st_theme_node_lookup_shadow (node,
"-st-background-image-shadow",
FALSE,
&shadow))
{
node->background_image_shadow = shadow;
return node->background_image_shadow;
}
return NULL;
}
@ -2732,43 +2889,25 @@ StShadow *
st_theme_node_get_text_shadow (StThemeNode *node)
{
StShadow *result = NULL;
int i;
if (node->text_shadow_computed)
return node->text_shadow;
ensure_properties (node);
for (i = node->n_properties - 1; i >= 0; i--)
if (!st_theme_node_lookup_shadow (node,
"text-shadow",
FALSE,
&result))
{
CRDeclaration *decl = node->properties[i];
if (strcmp (decl->property->stryng->str, "text-shadow") == 0)
if (node->parent_node)
{
ClutterColor color;
gdouble xoffset;
gdouble yoffset;
gdouble blur;
gdouble spread;
parse_shadow_property (node, decl,
&color, &xoffset, &yoffset, &blur, &spread);
result = st_shadow_new (&color,
xoffset, yoffset,
blur, spread);
break;
result = st_theme_node_get_text_shadow (node->parent_node);
if (result)
st_shadow_ref (result);
}
}
if (!result && node->parent_node)
{
result = st_theme_node_get_text_shadow (node->parent_node);
if (result)
st_shadow_ref (result);
}
node->text_shadow = result;
node->text_shadow_computed = TRUE;
@ -3102,7 +3241,8 @@ st_theme_node_get_paint_box (StThemeNode *node,
const ClutterActorBox *actor_box,
ClutterActorBox *paint_box)
{
StShadow *shadow;
StShadow *box_shadow;
StShadow *background_image_shadow;
ClutterActorBox shadow_box;
int outline_width;
@ -3110,26 +3250,41 @@ st_theme_node_get_paint_box (StThemeNode *node,
g_return_if_fail (actor_box != NULL);
g_return_if_fail (paint_box != NULL);
shadow = st_theme_node_get_shadow (node);
box_shadow = st_theme_node_get_box_shadow (node);
background_image_shadow = st_theme_node_get_background_image_shadow (node);
outline_width = st_theme_node_get_outline_width (node);
if (!shadow && !outline_width)
*paint_box = *actor_box;
if (!box_shadow && !background_image_shadow && !outline_width)
return;
paint_box->x1 -= outline_width;
paint_box->x2 += outline_width;
paint_box->y1 -= outline_width;
paint_box->y2 += outline_width;
if (box_shadow)
{
*paint_box = *actor_box;
return;
st_shadow_get_box (box_shadow, actor_box, &shadow_box);
paint_box->x1 = MIN (paint_box->x1, shadow_box.x1);
paint_box->x2 = MAX (paint_box->x2, shadow_box.x2);
paint_box->y1 = MIN (paint_box->y1, shadow_box.y1);
paint_box->y2 = MAX (paint_box->y2, shadow_box.y2);
}
if (shadow)
st_shadow_get_box (shadow, actor_box, &shadow_box);
else
shadow_box = *actor_box;
if (background_image_shadow)
{
st_shadow_get_box (background_image_shadow, actor_box, &shadow_box);
paint_box->x1 = MIN (actor_box->x1 - outline_width, shadow_box.x1);
paint_box->x2 = MAX (actor_box->x2 + outline_width, shadow_box.x2);
paint_box->y1 = MIN (actor_box->y1 - outline_width, shadow_box.y1);
paint_box->y2 = MAX (actor_box->y2 + outline_width, shadow_box.y2);
paint_box->x1 = MIN (paint_box->x1, shadow_box.x1);
paint_box->x2 = MAX (paint_box->x2, shadow_box.x2);
paint_box->y1 = MIN (paint_box->y1, shadow_box.y1);
paint_box->y2 = MAX (paint_box->y2, shadow_box.y2);
}
}
/**
* st_theme_node_geometry_equal:
* @node: a #StThemeNode
@ -3240,8 +3395,17 @@ st_theme_node_paint_equal (StThemeNode *node,
if (border_image != NULL && !st_border_image_equal (border_image, other_border_image))
return FALSE;
shadow = st_theme_node_get_shadow (node);
other_shadow = st_theme_node_get_shadow (other);
shadow = st_theme_node_get_box_shadow (node);
other_shadow = st_theme_node_get_box_shadow (other);
if ((shadow == NULL) != (other_shadow == NULL))
return FALSE;
if (shadow != NULL && !st_shadow_equal (shadow, other_shadow))
return FALSE;
shadow = st_theme_node_get_background_image_shadow (node);
other_shadow = st_theme_node_get_background_image_shadow (other);
if ((shadow == NULL) != (other_shadow == NULL))
return FALSE;

View File

@ -133,6 +133,10 @@ gboolean st_theme_node_lookup_length (StThemeNode *node,
const char *property_name,
gboolean inherit,
gdouble *length);
gboolean st_theme_node_lookup_shadow (StThemeNode *node,
const char *property_name,
gboolean inherit,
StShadow **shadow);
/* Easier-to-use variants of the above, for application-level use */
void st_theme_node_get_color (StThemeNode *node,
@ -142,6 +146,8 @@ gdouble st_theme_node_get_double (StThemeNode *node,
const char *property_name);
gdouble st_theme_node_get_length (StThemeNode *node,
const char *property_name);
StShadow *st_theme_node_get_shadow (StThemeNode *node,
const char *property_name);
/* Specific getters for particular properties: cached
*/
@ -195,9 +201,11 @@ StTextAlign st_theme_node_get_text_align (StThemeNode *node);
const PangoFontDescription *st_theme_node_get_font (StThemeNode *node);
StBorderImage *st_theme_node_get_border_image (StThemeNode *node);
StShadow *st_theme_node_get_shadow (StThemeNode *node);
StShadow *st_theme_node_get_box_shadow (StThemeNode *node);
StShadow *st_theme_node_get_text_shadow (StThemeNode *node);
StShadow *st_theme_node_get_background_image_shadow (StThemeNode *node);
StIconColors *st_theme_node_get_icon_colors (StThemeNode *node);
/* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */

View File

@ -357,13 +357,10 @@ st_widget_allocate (ClutterActor *actor,
{
StWidget *self = ST_WIDGET (actor);
StWidgetPrivate *priv = self->priv;
StThemeNode *theme_node;
ClutterActorClass *klass;
ClutterGeometry area;
ClutterVertex in_v, out_v;
theme_node = st_widget_get_theme_node (self);
klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
klass->allocate (actor, box, flags);
@ -686,15 +683,23 @@ st_widget_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume)
{
ClutterActorBox paint_box, alloc_box;
StThemeNode *theme_node;
StWidgetPrivate *priv;
ClutterVertex origin;
/* Setting the paint volume does not make sense when we don't have any allocation */
if (!clutter_actor_has_allocation (self))
return FALSE;
priv = ST_WIDGET(self)->priv;
theme_node = st_widget_get_theme_node (ST_WIDGET(self));
clutter_actor_get_allocation_box (self, &alloc_box);
st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box);
if (priv->transition_animation)
st_theme_node_transition_get_paint_box (priv->transition_animation,
&alloc_box, &paint_box);
else
st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box);
origin.x = paint_box.x1 - alloc_box.x1;
origin.y = paint_box.y1 - alloc_box.y1;

View File

@ -15,6 +15,7 @@ TEST_JS = \
interactive/scroll-view-sizing.js \
interactive/table.js \
testcommon/border-image.png \
testcommon/face-plain.png \
testcommon/ui.js \
unit/format.js
EXTRA_DIST += $(TEST_JS)

View File

@ -71,6 +71,65 @@ box.add(new St.Label({ text: "Border Image",
style_class: "border-image",
style: "padding: 10px;" }));
box.add(new St.Label({ text: "Border Image with Gradient",
style_class: 'border-image-with-background-gradient',
style: "padding: 10px;"
+ 'background-gradient-direction: vertical;' }));
box.add(new St.Label({ text: "Rounded, framed, shadowed gradients" }));
let framedGradients = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 12px;' });
box.add(framedGradients);
function addGradientCase(direction, borderWidth, borderRadius, extra) {
let gradientBox = new St.BoxLayout({ style_class: 'background-gradient',
style: 'border: ' + borderWidth + 'px solid #8b8b8b;'
+ 'border-radius: ' + borderRadius + 'px;'
+ 'background-gradient-direction: ' + direction + ';'
+ 'width: 32px;'
+ 'height: 32px;'
+ extra });
framedGradients.add(gradientBox, { x_fill: false, y_fill: false } );
}
addGradientCase ('horizontal', 0, 5, 'box-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addGradientCase ('horizontal', 2, 5, 'box-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addGradientCase ('horizontal', 5, 2, 'box-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addGradientCase ('horizontal', 5, 20, 'box-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addGradientCase ('vertical', 0, 5, 'box-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 2, 5, 'box-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 2, 'box-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 20, 'box-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
box.add(new St.Label({ text: "Rounded, framed, shadowed images" }));
let framedImages = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 6px;' });
box.add(framedImages);
function addBackgroundImageCase(borderWidth, borderRadius, width, height, extra) {
let imageBox = new St.BoxLayout({ style_class: 'background-image',
style: 'border: ' + borderWidth + 'px solid #8b8b8b;'
+ 'border-radius: ' + borderRadius + 'px;'
+ 'width: ' + width + 'px;'
+ 'height: ' + height + 'px;'
+ extra });
framedImages.add(imageBox, { x_fill: false, y_fill: false } );
}
addBackgroundImageCase (0, 0, 32, 32, 'background-position: 2px 5px');
addBackgroundImageCase (0, 0, 16, 16, '-st-background-image-shadow: 1px 1px 4px 0px rgba(0,0,0,0.5); background-color: rgba(0,0,0,0)');
addBackgroundImageCase (0, 5, 32, 32, '-st-background-image-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (2, 5, 32, 32, '-st-background-image-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addBackgroundImageCase (5, 2, 32, 32, '-st-background-image-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addBackgroundImageCase (5, 20, 32, 32, '-st-background-image-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addBackgroundImageCase (0, 5, 48, 48, '-st-background-image-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 48, 48, '-st-background-image-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 64, 64, '-st-background-image-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 64, 64, '-st-background-image-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 32, 32, 'background-position: 2px 5px');
stage.show();
Clutter.main();
stage.destroy();

View File

@ -319,17 +319,17 @@ function togglePolicy(button) {
hpolicy.connect('clicked', function() { togglePolicy(hpolicy); });
vpolicy.connect('clicked', function() { togglePolicy(vpolicy); });
let shadowsBox = new St.BoxLayout({ vertical: false });
mainBox.add(shadowsBox);
let fadeBox = new St.BoxLayout({ vertical: false });
mainBox.add(fadeBox);
spacer = new St.Bin();
shadowsBox.add(spacer, { expand: true });
fadeBox.add(spacer, { expand: true });
shadowsBox.add(new St.Label({ text: 'Vertical Shadows: '}));
let vshadows = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
shadowsBox.add(vshadows);
fadeBox.add(new St.Label({ text: 'Vertical Fade: '}));
let vfade = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
fadeBox.add(vfade);
function toggleShadows(button) {
function toggleFade(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
@ -338,10 +338,10 @@ function toggleShadows(button) {
button.label = 'No';
break;
}
scrollView.set_vshadows(vshadows.label == 'Yes');
scrollView.set_vfade(vfade.label == 'Yes');
}
vshadows.connect('clicked', function() { toggleShadows(vshadows); });
vfade.connect('clicked', function() { toggleFade(vfade); });
stage.show();
Clutter.main();

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -37,6 +37,23 @@ stage {
border-image: url('border-image.png') 16;
}
.background-gradient {
background-gradient-start: #88ff88;
background-gradient-end: #8888ff;
}
.border-image-with-background-gradient {
border: 15px black solid;
border-image: url('border-image.png') 16;
background-gradient-start: #88ff88;
background-gradient-end: #8888ff;
}
.background-image {
background-image: url('face-plain.png');
background-color: white;
}
.push-button {
background: #eeddbb;
border: 1px solid black;