Compare commits
103 Commits
shell-tool
...
2.28.0
Author | SHA1 | Date | |
---|---|---|---|
a26b0b60d1 | |||
60dbb19c2d | |||
3703a86354 | |||
992b43f914 | |||
ff39edd1ee | |||
a81a16801d | |||
4c6f770dea | |||
c0b01c0210 | |||
400326e549 | |||
d7af6d40e3 | |||
90ebcd32e3 | |||
53d0581377 | |||
439daf828f | |||
d120d03de9 | |||
53fbabe2ca | |||
4bdd40911f | |||
ff4ac0d02e | |||
45dd342cc0 | |||
e5efecd2bd | |||
3bf88b8988 | |||
4ddc1118bb | |||
fd1e7b2a0f | |||
aed1e67add | |||
dc99e8ffcd | |||
6a8b50cb00 | |||
edb50d5dc7 | |||
2f6c951997 | |||
64cd51667d | |||
caa08f27fa | |||
26015ef16d | |||
4798ad5107 | |||
9b05304c2d | |||
795feca393 | |||
31663dcd83 | |||
2f2df61093 | |||
3b8d53060d | |||
c5ce405859 | |||
b3a5fc72fb | |||
8a2cc11cc0 | |||
cbb3a3aec8 | |||
e382da9708 | |||
ab1fbbde92 | |||
5f5266ca60 | |||
68e8b14b8b | |||
abdd8b330c | |||
eb8176deeb | |||
956f89f377 | |||
1c69380923 | |||
d2bc7b200e | |||
afb3b1e718 | |||
061a2cfbfb | |||
243824ab80 | |||
90ddad7ba1 | |||
20b29ff48f | |||
96f4d318c5 | |||
6f7da264ba | |||
640e45c12a | |||
04e28cd7c4 | |||
2cc41c6726 | |||
03a45b665c | |||
5a42179a96 | |||
af3ec56ca1 | |||
289b19aa31 | |||
91eb613d69 | |||
1c7c53d19f | |||
d4304495c6 | |||
b77b205d37 | |||
a15205e6c4 | |||
28dbf7a06e | |||
1ec8e9eb6b | |||
fa09f7a6da | |||
d67e54d3ee | |||
d263c12e2e | |||
4d55ccff39 | |||
2a0adc0fc8 | |||
076e902b2c | |||
8c72623da3 | |||
1fd25573e5 | |||
6527dbc8b7 | |||
3c646ec516 | |||
a9fd350396 | |||
e91e8e993d | |||
276d9a9302 | |||
6b95864076 | |||
d4c577a299 | |||
b90fc1e194 | |||
55497899dd | |||
e37790fdf9 | |||
83402957bb | |||
58325fca76 | |||
8b6962f3bf | |||
c1f91def74 | |||
529f74c0e5 | |||
459a3b18f2 | |||
ac2be7f0d1 | |||
f6b80d5ed4 | |||
d291e568fd | |||
2b78d5bd5d | |||
c9d9846759 | |||
6baafaa530 | |||
c2af05f753 | |||
97df305a6d | |||
fee385ba35 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -38,5 +38,7 @@ src/gnomeshell-taskpanel
|
||||
src/gnome-shell
|
||||
src/test-recorder
|
||||
src/test-recorder.ogg
|
||||
src/test-theme
|
||||
stamp-h1
|
||||
tests/run-test.sh
|
||||
xmldocs.make
|
||||
|
17
Makefile.am
17
Makefile.am
@ -1,13 +1,22 @@
|
||||
SUBDIRS = data js src po
|
||||
SUBDIRS = data js src tests po
|
||||
|
||||
EXTRA_DIST = \
|
||||
.project \
|
||||
.settings
|
||||
.settings \
|
||||
autogen.sh
|
||||
|
||||
# These are files checked into Git that we don't want to distribute
|
||||
DIST_EXCLUDE = \
|
||||
.gitignore \
|
||||
gnome-shell.doap \
|
||||
MAINTAINERS \
|
||||
tools/build/*
|
||||
|
||||
distcheck-hook:
|
||||
@echo "Checking disted javascript against files in git"
|
||||
@echo "Checking disted files against files in git"
|
||||
@failed=false; \
|
||||
for f in `cd $(srcdir) && git ls-files js` ; do \
|
||||
exclude=`(for p in $(DIST_EXCLUDE) ; do echo --exclude=$$p ; done)`; \
|
||||
for f in `cd $(srcdir) && git ls-files $$exclude` ; do \
|
||||
if ! test -e $(distdir)/$$f ; then \
|
||||
echo File missing from distribution: $$f ; \
|
||||
failed=true ; \
|
||||
|
@ -1,4 +1,4 @@
|
||||
AC_INIT(gnome-shell, 2.27.3)
|
||||
AC_INIT(gnome-shell, 2.28.0)
|
||||
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
|
||||
@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
|
||||
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
|
||||
gobject-introspection-1.0 >= 0.6.5)
|
||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
|
||||
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
|
||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
||||
@ -128,5 +129,6 @@ AC_OUTPUT([
|
||||
js/misc/Makefile
|
||||
js/ui/Makefile
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
po/Makefile.in
|
||||
])
|
||||
|
@ -12,18 +12,26 @@ gnome-shell.desktop.in: gnome-shell.desktop.in.in
|
||||
gnome-shell.desktop: gnome-shell.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
|
||||
imagedir = $(pkgdatadir)/images
|
||||
|
||||
dist_image_DATA = \
|
||||
add-workspace.svg \
|
||||
app-well-glow.png \
|
||||
back.svg \
|
||||
close.svg \
|
||||
close-black.svg \
|
||||
info.svg \
|
||||
magnifier.svg \
|
||||
imagesdir = $(pkgdatadir)/images
|
||||
dist_images_DATA = \
|
||||
add-workspace.svg \
|
||||
app-well-glow.png \
|
||||
back.svg \
|
||||
close.svg \
|
||||
close-black.svg \
|
||||
info.svg \
|
||||
magnifier.svg \
|
||||
remove-workspace.svg
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
theme/gnome-shell.css \
|
||||
theme/scroll-button-down.png \
|
||||
theme/scroll-button-down-hover.png \
|
||||
theme/scroll-button-up.png \
|
||||
theme/scroll-button-up-hover.png \
|
||||
theme/scroll-vhandle.png
|
||||
|
||||
schemadir = @GCONF_SCHEMA_FILE_DIR@
|
||||
schema_DATA = gnome-shell.schemas
|
||||
|
||||
|
176
data/theme/gnome-shell.css
Normal file
176
data/theme/gnome-shell.css
Normal file
@ -0,0 +1,176 @@
|
||||
/* Copyright 2009, Red Hat, Inc.
|
||||
*
|
||||
* Portions adapted from Mx's data/style/default.css
|
||||
* Copyright 2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
StScrollBar
|
||||
{
|
||||
background-color: #354761;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
StScrollView
|
||||
{
|
||||
scrollbar-width: 16px;
|
||||
scrollbar-height: 16px;
|
||||
}
|
||||
|
||||
StButton#up-stepper
|
||||
{
|
||||
border-image: url("scroll-button-up.png") 5;
|
||||
}
|
||||
|
||||
StButton#up-stepper:hover,
|
||||
StButton#up-stepper:active
|
||||
{
|
||||
border-image: url("scroll-button-up-hover.png") 5;
|
||||
}
|
||||
|
||||
StButton#down-stepper
|
||||
{
|
||||
border-image: url("scroll-button-down.png") 5;
|
||||
}
|
||||
|
||||
StButton#down-stepper:hover,
|
||||
StButton#down-stepper:active
|
||||
{
|
||||
border-image: url("scroll-button-down-hover.png") 5;
|
||||
}
|
||||
|
||||
StScrollBar StButton#vhandle
|
||||
{
|
||||
border-image: url("scroll-vhandle.png") 5;
|
||||
}
|
||||
|
||||
StScrollBar StButton#vhandle:hover
|
||||
{
|
||||
border-image: url("scroll-vhandle.png") 5;
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
|
||||
.panel-button {
|
||||
padding: 4px 12px 3px;
|
||||
border-radius: 5px;
|
||||
font: 16px sans-serif;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.panel-button:active, .panel-button:checked {
|
||||
background-color: #314a6c;
|
||||
}
|
||||
|
||||
/* LookingGlass */
|
||||
|
||||
#LookingGlassDialog
|
||||
{
|
||||
background-color: rgba(0,0,0,0.85);
|
||||
spacing: 4px;
|
||||
padding: 4px;
|
||||
border: 1px solid rgba(0,0,172,0.85);
|
||||
border-radius: 4px;
|
||||
|
||||
color: #88ff66;
|
||||
}
|
||||
|
||||
#LookingGlassDialog > #Toolbar
|
||||
{
|
||||
border: 1px solid grey;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog StLabel
|
||||
{
|
||||
color: #88ff66;
|
||||
}
|
||||
|
||||
#LookingGlassDialog StEntry
|
||||
{
|
||||
color: #88ff66;
|
||||
}
|
||||
|
||||
#LookingGlassDialog StBoxLayout#EvalBox
|
||||
{
|
||||
padding: 4px;
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
/* Calendar popup */
|
||||
|
||||
#calendarPopup {
|
||||
border-radius: 5px;
|
||||
background: rgba(0,0,0,0.9);
|
||||
border: 1px solid rgba(128,128,128,0.45);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#calendarPopup .calendar {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
spacing-rows: 5px;
|
||||
spacing-columns: 3px;
|
||||
}
|
||||
|
||||
.calendar-change-month {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.calendar-change-month:hover {
|
||||
background: #314a6c;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-change-month:active {
|
||||
background: #213050;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
.calendar-today {
|
||||
font-weight: bold;
|
||||
background: #ffffff;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-list {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 1px solid rgba(128,128,128,0.40);
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.switcher-list .item-box {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.switcher-list .selected-item-box {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.33);
|
||||
}
|
BIN
data/theme/scroll-button-down-hover.png
Normal file
BIN
data/theme/scroll-button-down-hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 225 B |
BIN
data/theme/scroll-button-down.png
Normal file
BIN
data/theme/scroll-button-down.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 225 B |
BIN
data/theme/scroll-button-up-hover.png
Normal file
BIN
data/theme/scroll-button-up-hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 211 B |
BIN
data/theme/scroll-button-up.png
Normal file
BIN
data/theme/scroll-button-up.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 211 B |
BIN
data/theme/scroll-vhandle.png
Normal file
BIN
data/theme/scroll-vhandle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 323 B |
@ -1,4 +1,5 @@
|
||||
jsmiscdir = $(pkgdatadir)/js/misc
|
||||
|
||||
dist_jsmisc_DATA = \
|
||||
docInfo.js
|
||||
docInfo.js \
|
||||
format.js
|
||||
|
44
js/misc/format.js
Normal file
44
js/misc/format.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
* This function is intended to extend the String object and provide
|
||||
* an String.format API for string formatting.
|
||||
* It has to be set up using String.prototype.format = Format.format;
|
||||
* Usage:
|
||||
* "somestring %s %d".format('hello', 5);
|
||||
* It supports %s, %d and %f, for %f it also support precisions like
|
||||
* "%.2f".format(1.526)
|
||||
*/
|
||||
|
||||
function format() {
|
||||
let str = this;
|
||||
let i = 0;
|
||||
let args = arguments;
|
||||
|
||||
return str.replace(/%(?:\.([0-9]+))?(.)/g, function (str, precisionGroup, genericGroup) {
|
||||
|
||||
if (precisionGroup != '' && genericGroup != 'f')
|
||||
throw new Error("Precision can only be specified for 'f'");
|
||||
|
||||
switch (genericGroup) {
|
||||
case '%':
|
||||
return '%';
|
||||
break;
|
||||
case 's':
|
||||
return args[i++].toString();
|
||||
break;
|
||||
case 'd':
|
||||
return parseInt(args[i++]);
|
||||
break;
|
||||
case 'f':
|
||||
if (precisionGroup == '')
|
||||
return parseFloat(args[i++]);
|
||||
else
|
||||
return parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported conversion character %' + genericGroup);
|
||||
}
|
||||
return ""; // Suppress warning
|
||||
});
|
||||
}
|
@ -5,10 +5,12 @@ dist_jsui_DATA = \
|
||||
appDisplay.js \
|
||||
appIcon.js \
|
||||
button.js \
|
||||
calendar.js \
|
||||
chrome.js \
|
||||
dash.js \
|
||||
dnd.js \
|
||||
docDisplay.js \
|
||||
environment.js \
|
||||
genericDisplay.js \
|
||||
lightbox.js \
|
||||
link.js \
|
||||
|
794
js/ui/altTab.js
794
js/ui/altTab.js
@ -4,84 +4,61 @@ const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AppIcon = imports.ui.appIcon;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const POPUP_BG_COLOR = new Clutter.Color();
|
||||
POPUP_BG_COLOR.from_pixel(0x00000080);
|
||||
const POPUP_APPICON_BORDER_COLOR = new Clutter.Color();
|
||||
POPUP_APPICON_BORDER_COLOR.from_pixel(0xffffffff);
|
||||
const POPUP_ARROW_COLOR = new Clutter.Color();
|
||||
POPUP_ARROW_COLOR.from_pixel(0xffffffff);
|
||||
const TRANSPARENT_COLOR = new Clutter.Color();
|
||||
TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||
const POPUP_SEPARATOR_COLOR = new Clutter.Color();
|
||||
POPUP_SEPARATOR_COLOR.from_pixel(0x80808066);
|
||||
|
||||
const POPUP_GRID_SPACING = 8;
|
||||
const POPUP_ICON_SIZE = 48;
|
||||
const POPUP_NUM_COLUMNS = 5;
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_LIST_SPACING = 8;
|
||||
|
||||
const POPUP_POINTER_SELECTION_THRESHOLD = 3;
|
||||
|
||||
const THUMBNAIL_SIZE = 256;
|
||||
const THUMBNAIL_POPUP_TIME = 1000; // milliseconds
|
||||
|
||||
const HOVER_TIME = 500; // milliseconds
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
function AltTabPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
|
||||
corner_radius: POPUP_GRID_SPACING,
|
||||
padding: POPUP_GRID_SPACING,
|
||||
spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.VERTICAL,
|
||||
reactive: true });
|
||||
this.actor = new Clutter.Group({ reactive: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
// Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently
|
||||
// just implemented using a chain of Big.Box.
|
||||
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.VERTICAL });
|
||||
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._icons = [];
|
||||
this._currentWindows = [];
|
||||
this._haveModal = false;
|
||||
this._selected = 0;
|
||||
this._highlightedWindow = null;
|
||||
this._toplevels = global.window_group.get_children();
|
||||
|
||||
this._currentApp = 0;
|
||||
this._currentWindows = [];
|
||||
this._thumbnailTimeoutId = 0;
|
||||
|
||||
global.stage.add_actor(this.actor);
|
||||
},
|
||||
|
||||
_addIcon : function(appIcon) {
|
||||
appIcon.connect('activate', Lang.bind(this, this._appClicked));
|
||||
appIcon.connect('activate-window', Lang.bind(this, this._windowClicked));
|
||||
appIcon.connect('highlight-window', Lang.bind(this, this._windowHovered));
|
||||
appIcon.connect('menu-popped-up', Lang.bind(this, this._menuPoppedUp));
|
||||
appIcon.connect('menu-popped-down', Lang.bind(this, this._menuPoppedDown));
|
||||
|
||||
appIcon.actor.connect('enter-event', Lang.bind(this, this._iconEntered));
|
||||
|
||||
// FIXME?
|
||||
appIcon.actor.border = 2;
|
||||
appIcon.highlight_border_color = POPUP_APPICON_BORDER_COLOR;
|
||||
|
||||
this._icons.push(appIcon);
|
||||
this._currentWindows.push(appIcon.windows[0]);
|
||||
|
||||
// Add it to the grid
|
||||
if (!this._gridRow || this._gridRow.get_children().length == POPUP_NUM_COLUMNS) {
|
||||
this._gridRow = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
this._grid.append(this._gridRow, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
this._gridRow.append(appIcon.actor, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
show : function(initialSelection) {
|
||||
show : function(backward) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
let apps = appMonitor.get_running_apps ("");
|
||||
|
||||
@ -99,32 +76,52 @@ AltTabPopup.prototype = {
|
||||
this._mouseActive = false;
|
||||
this._mouseMovement = 0;
|
||||
|
||||
// Contruct the AppIcons, sort by time, add to the popup
|
||||
let icons = [];
|
||||
for (let i = 0; i < apps.length; i++)
|
||||
icons.push(new AppIcon.AppIcon(apps[i], AppIcon.MenuType.BELOW));
|
||||
icons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
for (let i = 0; i < icons.length; i++)
|
||||
this._addIcon(icons[i]);
|
||||
this._appSwitcher = new AppSwitcher(apps);
|
||||
this.actor.add_actor(this._appSwitcher.actor);
|
||||
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||
this._appSwitcher.connect('item-hovered', Lang.bind(this, this._appHovered));
|
||||
|
||||
// Need to specify explicit width and height because the
|
||||
// window_group may not actually cover the whole screen
|
||||
this._lightbox = new Lightbox.Lightbox(global.window_group,
|
||||
global.screen_width,
|
||||
global.screen_height);
|
||||
let primary = global.get_primary_monitor();
|
||||
this._appSwitcher.actor.x = primary.x + Math.floor((primary.width - this._appSwitcher.actor.width) / 2);
|
||||
this._appSwitcher.actor.y = primary.y + Math.floor((primary.height - this._appSwitcher.actor.height) / 2);
|
||||
|
||||
this.actor.show_all();
|
||||
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
|
||||
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
|
||||
this._appIcons = this._appSwitcher.icons;
|
||||
|
||||
this._updateSelection(initialSelection);
|
||||
// _currentWindows give the index of the selected window for
|
||||
// each app; they all start at 0.
|
||||
this._currentWindows = this._appIcons.map(function (app) { return 0; });
|
||||
|
||||
// Make the initial selection
|
||||
if (this._appIcons.length == 1) {
|
||||
if (!backward && this._appIcons[0].windows.length > 1) {
|
||||
// For compatibility with the multi-app case below
|
||||
this._select(0, 1);
|
||||
} else
|
||||
this._select(0);
|
||||
} else if (backward) {
|
||||
this._select(this._appIcons.length - 1);
|
||||
} else {
|
||||
if (this._appIcons[0].windows.length > 1) {
|
||||
let curAppNextWindow = this._appIcons[0].windows[1];
|
||||
let nextAppWindow = this._appIcons[1].windows[0];
|
||||
|
||||
// If the next window of the current app is more-recently-used
|
||||
// than the first window of the next app, then select it.
|
||||
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
|
||||
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
|
||||
this._select(0, 1);
|
||||
else
|
||||
this._select(1);
|
||||
} else
|
||||
this._select(1);
|
||||
}
|
||||
|
||||
// There's a race condition; if the user released Alt before
|
||||
// we got the grab, then we won't be notified. (See
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
|
||||
// details.) So we check now. (Have to do this after calling
|
||||
// _updateSelection.)
|
||||
let [screen, x, y, mods] = Gdk.Display.get_default().get_pointer();
|
||||
// details.) So we check now. (Have to do this after updating
|
||||
// selection.)
|
||||
let mods = global.get_modifier_keys();
|
||||
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
||||
this._finish();
|
||||
return false;
|
||||
@ -133,6 +130,502 @@ AltTabPopup.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
_nextApp : function() {
|
||||
return mod(this._currentApp + 1, this._appIcons.length);
|
||||
},
|
||||
_previousApp : function() {
|
||||
return mod(this._currentApp - 1, this._appIcons.length);
|
||||
},
|
||||
|
||||
_nextWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] + 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
},
|
||||
_previousWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] - 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
|
||||
// The WASD stuff is for debugging in Xephyr, where the arrow
|
||||
// keys aren't mapped correctly
|
||||
|
||||
if (keysym == Clutter.Tab)
|
||||
this._select(shift ? this._previousApp() : this._nextApp());
|
||||
else if (keysym == Clutter.grave)
|
||||
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
else if (this._thumbnails) {
|
||||
if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.Up || keysym == Clutter.w)
|
||||
this._select(this._currentApp, null, true);
|
||||
} else {
|
||||
if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||
this._select(this._previousApp());
|
||||
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||
this._select(this._nextApp());
|
||||
else if (keysym == Clutter.Down || keysym == Clutter.s)
|
||||
this._select(this._currentApp, this._currentWindows[this._currentApp]);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
|
||||
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_appActivated : function(appSwitcher, n) {
|
||||
Main.activateWindow(this._appIcons[n].windows[this._currentWindows[n]]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_appHovered : function(appSwitcher, n) {
|
||||
if (!this._mouseActive)
|
||||
return;
|
||||
|
||||
this._select(n, this._currentWindows[n]);
|
||||
},
|
||||
|
||||
_windowActivated : function(thumbnailList, n) {
|
||||
Main.activateWindow(this._appIcons[this._currentApp].windows[n]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowHovered : function(thumbnailList, n) {
|
||||
if (!this._mouseActive)
|
||||
return;
|
||||
|
||||
this._select(this._currentApp, n);
|
||||
},
|
||||
|
||||
_mouseMoved : function(actor, event) {
|
||||
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
|
||||
return;
|
||||
|
||||
this.actor.disconnect(this._motionEventId);
|
||||
this._mouseActive = true;
|
||||
|
||||
this._appSwitcher.checkHover();
|
||||
if (this._thumbnails)
|
||||
this._thumbnails.checkHover();
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
let app = this._appIcons[this._currentApp];
|
||||
let window = app.windows[this._currentWindows[this._currentApp]];
|
||||
Main.activateWindow(window);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
if (this._haveModal)
|
||||
Main.popModal(this.actor);
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
|
||||
if (this._thumbnailTimeoutId != 0)
|
||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||
},
|
||||
|
||||
_select : function(app, window, noTimeout) {
|
||||
if ((app != this._currentApp || !window) && this._thumbnails) {
|
||||
this._thumbnails.actor.destroy();
|
||||
this._thumbnails = null;
|
||||
this._appSwitcher.showArrow(-1);
|
||||
}
|
||||
|
||||
if (this._thumbnailTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||
this._thumbnailTimeoutId = 0;
|
||||
}
|
||||
|
||||
this._currentApp = app;
|
||||
if (window != null) {
|
||||
this._appSwitcher.highlight(-1);
|
||||
this._appSwitcher.showArrow(app);
|
||||
} else {
|
||||
this._appSwitcher.highlight(app);
|
||||
if (this._appIcons[this._currentApp].windows.length > 1)
|
||||
this._appSwitcher.showArrow(app);
|
||||
}
|
||||
|
||||
if (window != null) {
|
||||
if (!this._thumbnails)
|
||||
this._createThumbnails();
|
||||
this._currentWindows[this._currentApp] = window;
|
||||
this._thumbnails.highlight(window);
|
||||
} else if (this._appIcons[this._currentApp].windows.length > 1 &&
|
||||
!noTimeout) {
|
||||
this._thumbnailTimeoutId = Mainloop.timeout_add (
|
||||
THUMBNAIL_POPUP_TIME,
|
||||
Lang.bind(this, function () {
|
||||
this._select(this._currentApp,
|
||||
this._currentWindows[this._currentApp]);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_createThumbnails : function() {
|
||||
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].windows);
|
||||
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
|
||||
this._thumbnails.connect('item-hovered', Lang.bind(this, this._windowHovered));
|
||||
|
||||
this.actor.add_actor(this._thumbnails.actor);
|
||||
|
||||
let thumbnailCenter;
|
||||
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
|
||||
// Center the thumbnails under the corresponding AppIcon.
|
||||
// If this is being called when the switcher is first
|
||||
// being brought up, then nothing will have been assigned
|
||||
// an allocation yet, and the get_transformed_position()
|
||||
// call will return 0,0.
|
||||
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
|
||||
// Calling clutter_actor_get_allocation_box() would force
|
||||
// it to properly allocate itself, but we can't call that
|
||||
// because it has an out-caller-allocates arg. So we use
|
||||
// clutter_stage_get_actor_at_pos(), which will force a
|
||||
// reallocation as a side effect.
|
||||
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
||||
|
||||
let icon = this._appIcons[this._currentApp].actor;
|
||||
let [stageX, stageY] = icon.get_transformed_position();
|
||||
thumbnailCenter = stageX + icon.width / 2;
|
||||
} else {
|
||||
// Center the thumbnails on the monitor
|
||||
let primary = global.get_primary_monitor();
|
||||
thumbnailCenter = primary.x + primary.width / 2;
|
||||
}
|
||||
|
||||
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
|
||||
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
|
||||
}
|
||||
};
|
||||
|
||||
function SwitcherList(squareItems) {
|
||||
this._init(squareItems);
|
||||
}
|
||||
|
||||
SwitcherList.prototype = {
|
||||
_init : function(squareItems) {
|
||||
this.actor = new St.Bin({ style_class: 'switcher-list' });
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
// Here we use a GenericContainer so that we can force all the
|
||||
// children except the separator to have the same width.
|
||||
this._list = new Shell.GenericContainer();
|
||||
this._list.spacing = POPUP_LIST_SPACING;
|
||||
|
||||
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this._list.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
this.actor.add_actor(this._list);
|
||||
|
||||
this._items = [];
|
||||
this._highlighted = -1;
|
||||
this._separator = null;
|
||||
this._squareItems = squareItems;
|
||||
|
||||
this._hoverTimeout = 0;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._hoverTimeout != 0) {
|
||||
Mainloop.source_remove(this._hoverTimeout);
|
||||
this._hoverTimeout = 0;
|
||||
}
|
||||
},
|
||||
|
||||
addItem : function(item) {
|
||||
let box = new St.Bin({ style_class: 'item-box' });
|
||||
let bbox;
|
||||
|
||||
if (item instanceof Shell.ButtonBox)
|
||||
bbox = item;
|
||||
else {
|
||||
bbox = new Shell.ButtonBox({ reactive: true });
|
||||
bbox.append(item, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
box.add_actor(bbox);
|
||||
this._list.add_actor(box);
|
||||
|
||||
let n = this._items.length;
|
||||
bbox.connect('activate', Lang.bind(this, function () {
|
||||
this._itemActivated(n);
|
||||
}));
|
||||
bbox.connect('notify::hover', Lang.bind(this, function () {
|
||||
this._hoverChanged(bbox, n);
|
||||
}));
|
||||
|
||||
this._items.push(box);
|
||||
},
|
||||
|
||||
addSeparator: function () {
|
||||
// FIXME: make this work with StWidgets and CSS
|
||||
let box = new Big.Box({ padding_top: 2, padding_bottom: 2 });
|
||||
box.append(new Clutter.Rectangle({ width: 1,
|
||||
color: POPUP_SEPARATOR_COLOR }),
|
||||
Big.BoxPackFlags.EXPAND);
|
||||
this._separator = box;
|
||||
this._list.add_actor(box);
|
||||
},
|
||||
|
||||
highlight: function(index) {
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'item-box';
|
||||
|
||||
this._highlighted = index;
|
||||
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||
},
|
||||
|
||||
// Used after the mouse movement exceeds the threshold, to check
|
||||
// if it's already hovering over an icon
|
||||
checkHover: function() {
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].get_child().hover) {
|
||||
this._hoverChanged(this._items[i].get_child(), i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_itemActivated: function(n) {
|
||||
this.emit('item-activated', n);
|
||||
},
|
||||
|
||||
_hoverChanged: function(box, n) {
|
||||
if (this._hoverTimeout != 0) {
|
||||
Mainloop.source_remove(this._hoverTimeout);
|
||||
this._hoverTimeout = 0;
|
||||
}
|
||||
|
||||
if (box.hover) {
|
||||
this._hoverTimeout = Mainloop.timeout_add(
|
||||
HOVER_TIME,
|
||||
Lang.bind (this, function () {
|
||||
this._itemHovered(n);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_itemHovered: function(n) {
|
||||
this.emit('item-hovered', n);
|
||||
},
|
||||
|
||||
_maxChildWidth: function (forHeight) {
|
||||
let maxChildMin = 0;
|
||||
let maxChildNat = 0;
|
||||
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
|
||||
if (this._squareItems) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
}
|
||||
|
||||
return [maxChildMin, maxChildNat];
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
|
||||
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
|
||||
separatorWidth = sepNat + this._list.spacing;
|
||||
}
|
||||
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
|
||||
alloc.nat_size = this._items.length * maxChildNat + separatorWidth + totalSpacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
let maxChildMin = 0;
|
||||
let maxChildNat = 0;
|
||||
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
|
||||
if (this._squareItems) {
|
||||
let [childMin, childNat] = this._maxChildWidth(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
|
||||
alloc.min_size = maxChildMin;
|
||||
alloc.nat_size = maxChildNat;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childHeight = box.y2 - box.y1;
|
||||
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
|
||||
separatorWidth = sepNat;
|
||||
totalSpacing += this._list.spacing;
|
||||
}
|
||||
|
||||
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
|
||||
|
||||
let x = 0;
|
||||
let children = this._list.get_children();
|
||||
let childBox = new Clutter.ActorBox();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (this._items.indexOf(children[i]) != -1) {
|
||||
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
|
||||
let vSpacing = (childHeight - childNat) / 2;
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = vSpacing;
|
||||
childBox.x2 = x + childWidth;
|
||||
childBox.y2 = childBox.y1 + childNat;
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
x += this._list.spacing + childWidth;
|
||||
} else if (children[i] == this._separator) {
|
||||
// We want the separator to be more compact than the rest.
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = x + separatorWidth;
|
||||
childBox.y2 = childHeight;
|
||||
children[i].allocate(childBox, flags);
|
||||
x += this._list.spacing + separatorWidth;
|
||||
} else {
|
||||
// Something else, eg, AppSwitcher's arrows;
|
||||
// we don't allocate it.
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(SwitcherList.prototype);
|
||||
|
||||
function AppSwitcher(apps) {
|
||||
this._init(apps);
|
||||
}
|
||||
|
||||
AppSwitcher.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(apps) {
|
||||
SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
// Construct the AppIcons, sort by time, add to the popup
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let workspaceIcons = [];
|
||||
let otherIcons = [];
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let appIcon = new AppIcon.AppIcon({ appInfo: apps[i],
|
||||
size: POPUP_APPICON_SIZE });
|
||||
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
|
||||
workspaceIcons.push(appIcon);
|
||||
else
|
||||
otherIcons.push(appIcon);
|
||||
}
|
||||
|
||||
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
|
||||
this.icons = [];
|
||||
this._arrows = [];
|
||||
for (let i = 0; i < workspaceIcons.length; i++)
|
||||
this._addIcon(workspaceIcons[i]);
|
||||
if (workspaceIcons.length > 0 && otherIcons.length > 0)
|
||||
this.addSeparator();
|
||||
for (let i = 0; i < otherIcons.length; i++)
|
||||
this._addIcon(otherIcons[i]);
|
||||
|
||||
this._shownArrow = -1;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
// Allocate the main list items
|
||||
SwitcherList.prototype._allocate.call(this, actor, box, flags);
|
||||
|
||||
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
|
||||
let arrowWidth = arrowHeight * 2;
|
||||
|
||||
// Now allocate each arrow underneath its item
|
||||
let childBox = new Clutter.ActorBox();
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let itemBox = this._items[i].allocation;
|
||||
childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
|
||||
childBox.x2 = childBox.x1 + arrowWidth;
|
||||
childBox.y1 = itemBox.y2 + arrowHeight;
|
||||
childBox.y2 = childBox.y1 + arrowHeight;
|
||||
this._arrows[i].allocate(childBox, flags);
|
||||
}
|
||||
},
|
||||
|
||||
showArrow : function(n) {
|
||||
if (this._shownArrow != -1)
|
||||
this._arrows[this._shownArrow].hide();
|
||||
|
||||
this._shownArrow = n;
|
||||
|
||||
if (this._shownArrow != -1)
|
||||
this._arrows[this._shownArrow].show();
|
||||
},
|
||||
|
||||
_addIcon : function(appIcon) {
|
||||
this.icons.push(appIcon);
|
||||
this.addItem(appIcon.actor);
|
||||
|
||||
let arrow = new Shell.DrawingArea();
|
||||
arrow.connect('redraw', Lang.bind(this,
|
||||
function (area, texture) {
|
||||
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
|
||||
TRANSPARENT_COLOR,
|
||||
POPUP_ARROW_COLOR);
|
||||
}));
|
||||
this._list.add_actor(arrow);
|
||||
this._arrows.push(arrow);
|
||||
arrow.hide();
|
||||
},
|
||||
|
||||
_hasWindowsOnWorkspace: function(appIcon, workspace) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].get_workspace() == workspace)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_hasVisibleWindows : function(appIcon) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].showing_on_its_workspace())
|
||||
@ -155,133 +648,32 @@ AltTabPopup.prototype = {
|
||||
return (appIcon2.windows[0].get_user_time() -
|
||||
appIcon1.windows[0].get_user_time());
|
||||
}
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
||||
|
||||
if (keysym == Clutter.Tab)
|
||||
this._updateSelection(backwards ? -1 : 1);
|
||||
else if (keysym == Clutter.grave)
|
||||
this._updateWindowSelection(backwards ? -1 : 1);
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
|
||||
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_appClicked : function(icon) {
|
||||
Main.activateWindow(icon.windows[0]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowClicked : function(icon, window) {
|
||||
if (window)
|
||||
Main.activateWindow(window);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowHovered : function(icon, window) {
|
||||
if (window)
|
||||
this._highlightWindow(window);
|
||||
},
|
||||
|
||||
_mouseMoved : function(actor, event) {
|
||||
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
|
||||
return;
|
||||
|
||||
this.actor.disconnect(this._motionEventId);
|
||||
this._mouseActive = true;
|
||||
|
||||
actor = event.get_source();
|
||||
while (actor) {
|
||||
if (actor._delegate instanceof AppIcon.AppIcon) {
|
||||
this._iconEntered(actor, event);
|
||||
return;
|
||||
}
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
},
|
||||
|
||||
_iconEntered : function(actor, event) {
|
||||
let index = this._icons.indexOf(actor._delegate);
|
||||
if (this._mouseActive)
|
||||
this._updateSelection(index - this._selected);
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
if (this._highlightedWindow)
|
||||
Main.activateWindow(this._highlightedWindow);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
if (this._haveModal)
|
||||
Main.popModal(this.actor);
|
||||
|
||||
if (this._lightbox)
|
||||
this._lightbox.destroy();
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
},
|
||||
|
||||
_updateSelection : function(delta) {
|
||||
this._icons[this._selected].setHighlight(false);
|
||||
if (delta != 0 && this._selectedMenu)
|
||||
this._selectedMenu.popdown();
|
||||
|
||||
this._selected = (this._selected + this._icons.length + delta) % this._icons.length;
|
||||
this._icons[this._selected].setHighlight(true);
|
||||
|
||||
this._highlightWindow(this._currentWindows[this._selected]);
|
||||
},
|
||||
|
||||
_menuPoppedUp : function(icon, menu) {
|
||||
this._selectedMenu = menu;
|
||||
},
|
||||
|
||||
_menuPoppedDown : function(icon, menu) {
|
||||
this._selectedMenu = null;
|
||||
},
|
||||
|
||||
_updateWindowSelection : function(delta) {
|
||||
let icon = this._icons[this._selected];
|
||||
|
||||
if (!this._selectedMenu)
|
||||
icon.popupMenu();
|
||||
if (!this._selectedMenu)
|
||||
return;
|
||||
|
||||
let next = 0;
|
||||
for (let i = 0; i < icon.windows.length; i++) {
|
||||
if (icon.windows[i] == this._highlightedWindow) {
|
||||
next = (i + icon.windows.length + delta) % icon.windows.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._selectedMenu.selectWindow(icon.windows[next]);
|
||||
},
|
||||
|
||||
_highlightWindow : function(metaWin) {
|
||||
this._highlightedWindow = metaWin;
|
||||
this._currentWindows[this._selected] = metaWin;
|
||||
this._lightbox.highlight(this._highlightedWindow.get_compositor_private());
|
||||
}
|
||||
};
|
||||
|
||||
function ThumbnailList(windows) {
|
||||
this._init(windows);
|
||||
}
|
||||
|
||||
ThumbnailList.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(windows) {
|
||||
SwitcherList.prototype._init.call(this);
|
||||
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let mutterWindow = windows[i].get_compositor_private();
|
||||
let windowTexture = mutterWindow.get_texture ();
|
||||
let [width, height] = windowTexture.get_size();
|
||||
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
|
||||
|
||||
let clone = new Clutter.Clone ({ source: windowTexture,
|
||||
reactive: true,
|
||||
width: width * scale,
|
||||
height: height * scale });
|
||||
let box = new Big.Box({ padding: AppIcon.APPICON_BORDER_WIDTH + AppIcon.APPICON_PADDING });
|
||||
box.append(clone, Big.BoxPackFlags.NONE);
|
||||
this.addItem(box);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -177,31 +177,21 @@ AppDisplay.prototype = {
|
||||
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
|
||||
// map<itemId, array of category names>
|
||||
this._appCategories = {};
|
||||
// map<search term, map<appId, true>>
|
||||
// We use a map of appIds instead of an array to ensure that we don't have duplicates and for easier lookup.
|
||||
this._menuSearchAppMatches = {};
|
||||
|
||||
this._appMonitor = Shell.AppMonitor.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appsStale = true;
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
|
||||
this._appsStale = true;
|
||||
this._redisplay(0);
|
||||
this._redisplayMenus();
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
|
||||
this._redisplay(0);
|
||||
this._appsStale = true;
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
this._appMonitor.connect('app-added', Lang.bind(this, function(monitor) {
|
||||
this._redisplay(0);
|
||||
}));
|
||||
this._appMonitor.connect('app-removed', Lang.bind(this, function(monitor) {
|
||||
this._redisplay(0);
|
||||
}));
|
||||
|
||||
// Load the apps now so it doesn't slow down the first
|
||||
// transition into the Overview
|
||||
this._refreshCache();
|
||||
|
||||
this._focusInMenus = true;
|
||||
this._activeMenuIndex = -1;
|
||||
@ -210,7 +200,6 @@ AppDisplay.prototype = {
|
||||
this._menuDisplay = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: MENU_SPACING
|
||||
});
|
||||
this._redisplayMenus();
|
||||
|
||||
this.connect('expanded', Lang.bind(this, function (self) {
|
||||
this._filterReset();
|
||||
@ -253,6 +242,39 @@ AppDisplay.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
setSearch: function(text) {
|
||||
let lowertext = text.toLowerCase();
|
||||
if (lowertext == this._search)
|
||||
return;
|
||||
|
||||
// We prepare menu matches up-front, so that we don't
|
||||
// need to go over all menu items for each application
|
||||
// and then get all applications for a matching menu
|
||||
// to see if a particular application passed to
|
||||
// _isInfoMatching() is a match.
|
||||
let terms = lowertext.split(/\s+/);
|
||||
this._menuSearchAppMatches = {};
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
this._menuSearchAppMatches[term] = {};
|
||||
for (let j = 0; j < this._menus.length; j++) {
|
||||
let menuItem = this._menus[j];
|
||||
// Match only on the beginning of the words in category names,
|
||||
// because otherwise it introduces unnecessary noise in the results.
|
||||
if (menuItem.name.toLowerCase().indexOf(term) == 0 ||
|
||||
menuItem.name.toLowerCase().indexOf(" " + term) > 0) {
|
||||
let menuApps = this._appSystem.get_applications_for_menu(menuItem.id);
|
||||
for (let k = 0; k < menuApps.length; k++) {
|
||||
let menuApp = menuApps[k];
|
||||
this._menuSearchAppMatches[term][menuApp.get_id()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericDisplay.GenericDisplay.prototype.setSearch.call(this, text);
|
||||
},
|
||||
|
||||
// Protected overrides
|
||||
|
||||
_filterActive: function() {
|
||||
@ -303,7 +325,7 @@ AppDisplay.prototype = {
|
||||
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
|
||||
}
|
||||
}
|
||||
this._redisplay(true);
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.FULL);
|
||||
}));
|
||||
this._menuDisplay.append(display.actor, 0);
|
||||
},
|
||||
@ -333,7 +355,6 @@ AppDisplay.prototype = {
|
||||
if (!this._appsStale)
|
||||
return true;
|
||||
this._allItems = {};
|
||||
this._appCategories = {};
|
||||
|
||||
if (this._showPrefs) {
|
||||
// Get the desktop file ids for settings/preferences.
|
||||
@ -361,6 +382,7 @@ AppDisplay.prototype = {
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
this._redisplayMenus();
|
||||
}
|
||||
|
||||
this._appsStale = false;
|
||||
@ -383,7 +405,7 @@ AppDisplay.prototype = {
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name, description, execution command, and categories for the application.
|
||||
// the name, description, execution command, and category for the application.
|
||||
// Item info is expected to be Shell.AppInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching : function(itemInfo, search) {
|
||||
@ -395,10 +417,10 @@ AppDisplay.prototype = {
|
||||
if (this._activeMenu == null || search != "")
|
||||
return this._isInfoMatchingSearch(itemInfo, search);
|
||||
else
|
||||
return this._isInfoMatchingMenu(itemInfo, search);
|
||||
return this._isInfoMatchingMenu(itemInfo);
|
||||
},
|
||||
|
||||
_isInfoMatchingMenu : function(itemInfo, search) {
|
||||
_isInfoMatchingMenu: function(itemInfo) {
|
||||
let id = itemInfo.get_id();
|
||||
for (let i = 0; i < this._activeMenuApps.length; i++) {
|
||||
let activeApp = this._activeMenuApps[i];
|
||||
@ -436,11 +458,11 @@ AppDisplay.prototype = {
|
||||
return true;
|
||||
}
|
||||
|
||||
let categories = itemInfo.get_categories();
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
let category = fold(categories[i]);
|
||||
if (category.indexOf(search) >= 0)
|
||||
if (this._menuSearchAppMatches[search]) {
|
||||
if (this._menuSearchAppMatches[search].hasOwnProperty(itemInfo.get_id()))
|
||||
return true;
|
||||
} else {
|
||||
log("Missing an entry for search term " + search + " in this._menuSearchAppMatches");
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -461,8 +483,10 @@ function BaseWellItem(appInfo, isFavorite, hasMenu) {
|
||||
BaseWellItem.prototype = {
|
||||
__proto__: AppIcon.AppIcon.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite, hasMenu) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, appInfo, hasMenu ? AppIcon.MenuType.ON_RIGHT : AppIcon.MenuType.NONE);
|
||||
_init: function(appInfo, isFavorite) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, { appInfo: appInfo,
|
||||
menuType: AppIcon.MenuType.ON_RIGHT,
|
||||
glow: true });
|
||||
|
||||
this.isFavorite = isFavorite;
|
||||
|
||||
@ -521,7 +545,7 @@ RunningWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite, true);
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite);
|
||||
|
||||
this._dragStartX = 0;
|
||||
this._dragStartY = 0;
|
||||
@ -530,7 +554,7 @@ RunningWellItem.prototype = {
|
||||
},
|
||||
|
||||
_onActivate: function (actor, event) {
|
||||
let modifiers = event.get_state();
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
this.appInfo.launch();
|
||||
@ -594,6 +618,12 @@ InactiveWellItem.prototype = {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
menuPoppedUp: function() {
|
||||
},
|
||||
|
||||
menuPoppedDown: function() {
|
||||
}
|
||||
};
|
||||
|
||||
@ -661,10 +691,8 @@ WellGrid.prototype = {
|
||||
childBox.y2 = childBox.y1 + itemHeight;
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
let atSeparator = (i == this._separatorIndex - 1);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == columns || atSeparator) {
|
||||
if (columnIndex == columns) {
|
||||
columnIndex = 0;
|
||||
}
|
||||
|
||||
@ -674,27 +702,7 @@ WellGrid.prototype = {
|
||||
} else {
|
||||
x += itemWidth;
|
||||
}
|
||||
|
||||
if (atSeparator) {
|
||||
y += separatorNatural + WELL_ITEM_VSPACING;
|
||||
}
|
||||
}
|
||||
|
||||
let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
|
||||
|
||||
/* Allocate the separator */
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = box.x1;
|
||||
childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
|
||||
this._cachedSeparatorY = childBox.y1;
|
||||
childBox.x2 = box.x2;
|
||||
childBox.y2 = childBox.y1+separatorNatural;
|
||||
this._separator.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
setSeparatorIndex: function (index) {
|
||||
this._separatorIndex = index;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
@ -702,11 +710,6 @@ WellGrid.prototype = {
|
||||
for (let i = 0; i < itemChildren.length; i++) {
|
||||
itemChildren[i].destroy();
|
||||
}
|
||||
this._separatorIndex = 0;
|
||||
},
|
||||
|
||||
isBeforeSeparator: function(x, y) {
|
||||
return y < this._cachedSeparatorY;
|
||||
},
|
||||
|
||||
_getItemChildren: function () {
|
||||
@ -724,7 +727,10 @@ WellGrid.prototype = {
|
||||
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
if (forWidth < 0) {
|
||||
// Big.Box will allocate us at 0x0 if we are not visible; this is probably a
|
||||
// Big.Box bug but it can't be fixed because if children are skipped in allocate()
|
||||
// Clutter gets confused (see http://bugzilla.openedhand.com/show_bug.cgi?id=1831)
|
||||
if (forWidth <= 0) {
|
||||
nColumns = WELL_DEFAULT_COLUMNS;
|
||||
} else {
|
||||
while (nColumns < WELL_DEFAULT_COLUMNS &&
|
||||
@ -746,11 +752,10 @@ WellGrid.prototype = {
|
||||
let minWidth = itemMinWidth * nColumns;
|
||||
|
||||
let lastColumnIndex = nColumns - 1;
|
||||
let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
|
||||
let rows = Math.ceil((children.length + separatorColumns) / nColumns);
|
||||
let rows = Math.ceil(children.length / nColumns);
|
||||
|
||||
let itemWidth;
|
||||
if (forWidth < 0) {
|
||||
if (forWidth <= 0) {
|
||||
itemWidth = itemNaturalWidth;
|
||||
} else {
|
||||
itemWidth = Math.floor(forWidth / nColumns);
|
||||
@ -794,6 +799,9 @@ AppWell.prototype = {
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._pendingRedisplay = false;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
||||
|
||||
this._grid = new WellGrid();
|
||||
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
@ -833,7 +841,20 @@ AppWell.prototype = {
|
||||
values[id] = index; return values; }, {});
|
||||
},
|
||||
|
||||
_onMappedNotify: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._pendingRedisplay)
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._pendingRedisplay = true;
|
||||
return;
|
||||
}
|
||||
this._pendingRedisplay = false;
|
||||
|
||||
this._grid.removeAll();
|
||||
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
@ -849,7 +870,6 @@ AppWell.prototype = {
|
||||
|
||||
let displays = []
|
||||
this._addApps(favorites, true);
|
||||
this._grid.setSeparatorIndex(favorites.length);
|
||||
this._addApps(running, false);
|
||||
this._displays = displays;
|
||||
},
|
||||
@ -872,9 +892,7 @@ AppWell.prototype = {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
let app = null;
|
||||
if (source instanceof BaseWellItem) {
|
||||
app = source.appInfo;
|
||||
} else if (source instanceof AppDisplayItem) {
|
||||
if (source instanceof AppDisplayItem) {
|
||||
app = appSystem.lookup_cached_app(source.getId());
|
||||
} else if (source instanceof Workspaces.WindowClone) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
@ -891,22 +909,15 @@ AppWell.prototype = {
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
let favoriteIdsObject = this._arrayValues(favoriteIds);
|
||||
|
||||
let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
|
||||
y - this._grid.actor.y);
|
||||
let srcIsFavorite = (id in favoriteIdsObject);
|
||||
|
||||
if (srcIsFavorite && (!dropIsFavorite)) {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.remove_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else if ((!srcIsFavorite) && dropIsFavorite) {
|
||||
if (srcIsFavorite) {
|
||||
return false;
|
||||
} else {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.add_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
198
js/ui/appIcon.js
198
js/ui/appIcon.js
@ -20,7 +20,7 @@ GLOW_COLOR.from_pixel(0x4f6ba4ff);
|
||||
const GLOW_PADDING_HORIZONTAL = 3;
|
||||
const GLOW_PADDING_VERTICAL = 3;
|
||||
|
||||
const APPICON_ICON_SIZE = 48;
|
||||
const APPICON_DEFAULT_ICON_SIZE = 48;
|
||||
|
||||
const APPICON_PADDING = 1;
|
||||
const APPICON_BORDER_WIDTH = 1;
|
||||
@ -49,14 +49,19 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||
|
||||
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
|
||||
|
||||
function AppIcon(appInfo, menuType) {
|
||||
this._init(appInfo, menuType || MenuType.NONE);
|
||||
function AppIcon(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
_init : function(appInfo, menuType) {
|
||||
this.appInfo = appInfo;
|
||||
this._menuType = menuType;
|
||||
_init : function(params) {
|
||||
this.appInfo = params.appInfo;
|
||||
if (!this.appInfo)
|
||||
throw new Error('AppIcon constructor requires "appInfo" param');
|
||||
|
||||
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
|
||||
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
|
||||
let showGlow = ('glow' in params) ? params.glow : false;
|
||||
|
||||
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
border: APPICON_BORDER_WIDTH,
|
||||
@ -65,29 +70,36 @@ AppIcon.prototype = {
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
|
||||
this._signalIds = [];
|
||||
|
||||
if (menuType != MenuType.NONE) {
|
||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
|
||||
for (let i = 0; i < this.windows.length; i++) {
|
||||
this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
||||
}
|
||||
this._resortWindows();
|
||||
// Note, we don't presently update the window list dynamically here; this actor
|
||||
// gets destroyed and recreated by AppWell, and in alt-tab the whole thing is
|
||||
// created each time
|
||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(this.appInfo.get_id());
|
||||
for (let i = 0; i < this.windows.length; i++) {
|
||||
let sigId = this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
||||
this._signalIds.push([this.windows[i], sigId]);
|
||||
}
|
||||
this._resortWindows();
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._destroy));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedChanged));
|
||||
|
||||
if (this._menuType != MenuType.NONE) {
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
|
||||
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
|
||||
|
||||
this._menuTimeoutId = 0;
|
||||
this._menu = null;
|
||||
} else
|
||||
this.windows = [];
|
||||
}
|
||||
|
||||
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
width: APPICON_ICON_SIZE,
|
||||
height: APPICON_ICON_SIZE });
|
||||
this.icon = appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
||||
width: this._iconSize,
|
||||
height: this._iconSize });
|
||||
this.icon = this.appInfo.create_icon_texture(this._iconSize);
|
||||
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
|
||||
@ -102,18 +114,23 @@ AppIcon.prototype = {
|
||||
font_name: "Sans 12px",
|
||||
line_alignment: Pango.Alignment.CENTER,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: appInfo.get_name() });
|
||||
text: this.appInfo.get_name() });
|
||||
nameBox.add_actor(this._name);
|
||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
glowPath, -1, -1);
|
||||
glow.keep_aspect_ratio = false;
|
||||
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
||||
if (showGlow) {
|
||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
glowPath, -1, -1);
|
||||
glow.keep_aspect_ratio = false;
|
||||
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
||||
}
|
||||
this._nameBox.add_actor(this._glowBox);
|
||||
this._glowBox.lower(this._name);
|
||||
}
|
||||
this._nameBox.add_actor(this._glowBox);
|
||||
this._glowBox.lower(this._name);
|
||||
else
|
||||
this._glowBox = null;
|
||||
|
||||
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
@ -158,8 +175,36 @@ AppIcon.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_destroy: function() {
|
||||
for (let i = 0; i < this._signalIds.length; i++) {
|
||||
let [obj, sigId] = this._signalIds[i];
|
||||
obj.disconnect(sigId);
|
||||
}
|
||||
},
|
||||
|
||||
_onMappedChanged: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._windowSortStale)
|
||||
this._resortWindows();
|
||||
},
|
||||
|
||||
_resortWindows: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._windowSortStale = true;
|
||||
return;
|
||||
}
|
||||
this._windowSortStale = false;
|
||||
this.windows.sort(function (a, b) {
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let wsA = a.get_workspace() == activeWorkspace;
|
||||
let wsB = b.get_workspace() == activeWorkspace;
|
||||
|
||||
if (wsA && !wsB)
|
||||
return -1;
|
||||
else if (wsB && !wsA)
|
||||
return 1;
|
||||
|
||||
let visA = a.showing_on_its_workspace();
|
||||
let visB = b.showing_on_its_workspace();
|
||||
|
||||
@ -176,7 +221,7 @@ AppIcon.prototype = {
|
||||
// a subclass of it draggable, you can use this method to create
|
||||
// a drag actor
|
||||
createDragActor: function() {
|
||||
return this.appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
||||
return this.appInfo.create_icon_texture(this._iconSize);
|
||||
},
|
||||
|
||||
setHighlight: function(highlight) {
|
||||
@ -205,14 +250,19 @@ AppIcon.prototype = {
|
||||
},
|
||||
|
||||
_updateMenuOnButtonPress: function(actor, event) {
|
||||
if (this._menuTimeoutId != 0)
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
||||
Lang.bind(this, this.popupMenu));
|
||||
let button = event.get_button();
|
||||
if (button == 1) {
|
||||
if (this._menuTimeoutId != 0)
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
||||
Lang.bind(this, function () { this.popupMenu(button); }));
|
||||
} else if (button == 3) {
|
||||
this.popupMenu(button);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
popupMenu: function() {
|
||||
popupMenu: function(activatingButton) {
|
||||
if (this._menuTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = 0;
|
||||
@ -236,7 +286,7 @@ AppIcon.prototype = {
|
||||
}));
|
||||
}
|
||||
|
||||
this._menu.popup();
|
||||
this._menu.popup(activatingButton);
|
||||
|
||||
return false;
|
||||
},
|
||||
@ -299,7 +349,7 @@ AppIconMenu.prototype = {
|
||||
this._arrow = new Shell.DrawingArea();
|
||||
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
|
||||
Shell.draw_box_pointer(texture,
|
||||
this._type == MenuType.ON_RIGHT ? Clutter.Gravity.WEST : Clutter.Gravity.NORTH,
|
||||
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
|
||||
source.highlight_border_color,
|
||||
APPICON_MENU_BACKGROUND_COLOR);
|
||||
}));
|
||||
@ -377,32 +427,53 @@ AppIconMenu.prototype = {
|
||||
|
||||
let iconsDiffer = false;
|
||||
let texCache = Shell.TextureCache.get_default();
|
||||
let firstIcon = windows[0].mini_icon;
|
||||
for (let i = 1; i < windows.length; i++) {
|
||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
||||
iconsDiffer = true;
|
||||
break;
|
||||
if (windows.length > 0) {
|
||||
let firstIcon = windows[0].mini_icon;
|
||||
for (let i = 1; i < windows.length; i++) {
|
||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
||||
iconsDiffer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the app windows menu items and the separator between windows
|
||||
// of the current desktop and other windows.
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
|
||||
|
||||
let currentWorkspaceWindows = windows.filter(function (w) {
|
||||
return w.get_workspace() == activeWorkspace;
|
||||
});
|
||||
let otherWorkspaceWindows = windows.filter(function (w) {
|
||||
return w.get_workspace() != activeWorkspace;
|
||||
});
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
|
||||
this._appendSeparator();
|
||||
separatorShown = true;
|
||||
}
|
||||
|
||||
this._appendWindows(currentWorkspaceWindows, iconsDiffer);
|
||||
if (currentWorkspaceWindows.length > 0 && otherWorkspaceWindows.length > 0) {
|
||||
this._appendSeparator();
|
||||
let icon = null;
|
||||
if (iconsDiffer)
|
||||
icon = Shell.TextureCache.get_default().bind_pixbuf_property(windows[i], "mini-icon");
|
||||
|
||||
let box = this._appendMenuItem(icon, windows[i].title);
|
||||
box._window = windows[i];
|
||||
}
|
||||
this._appendWindows(otherWorkspaceWindows, iconsDiffer);
|
||||
|
||||
this._appendSeparator();
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
|
||||
this._newWindowMenuItem = this._appendMenuItem(null, _("New Window"));
|
||||
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
|
||||
|
||||
let favorites = Shell.AppSystem.get_default().get_favorites();
|
||||
let id = this._source.appInfo.get_id();
|
||||
this._isFavorite = false;
|
||||
for (let i = 0; i < favorites.length; i++) {
|
||||
if (id == favorites[i]) {
|
||||
this._isFavorite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(null, this._isFavorite ? _("Remove from favorites")
|
||||
: _("Add to favorites"));
|
||||
|
||||
this._highlightedItem = null;
|
||||
},
|
||||
@ -441,26 +512,13 @@ AppIconMenu.prototype = {
|
||||
return box;
|
||||
},
|
||||
|
||||
_appendWindows: function (windows, iconsDiffer) {
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let metaWindow = windows[i];
|
||||
|
||||
let icon = null;
|
||||
if (iconsDiffer) {
|
||||
icon = Shell.TextureCache.get_default().bind_pixbuf_property(metaWindow, "mini-icon");
|
||||
}
|
||||
let box = this._appendMenuItem(icon, metaWindow.title);
|
||||
box._window = metaWindow;
|
||||
}
|
||||
},
|
||||
|
||||
popup: function() {
|
||||
popup: function(activatingButton) {
|
||||
let [stageX, stageY] = this._source.actor.get_transformed_position();
|
||||
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
|
||||
|
||||
this._redisplay();
|
||||
|
||||
this._windowContainer.popup(0, Main.currentTime());
|
||||
this._windowContainer.popup(activatingButton, Main.currentTime());
|
||||
|
||||
this.emit('popup', true);
|
||||
|
||||
@ -559,6 +617,12 @@ AppIconMenu.prototype = {
|
||||
} else if (child == this._newWindowMenuItem) {
|
||||
this._source.appInfo.launch();
|
||||
this.emit('activate-window', null);
|
||||
} else if (child == this._toggleFavoriteMenuItem) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
if (this._isFavorite)
|
||||
appSys.remove_favorite(this._source.appInfo.get_id());
|
||||
else
|
||||
appSys.add_favorite(this._source.appInfo.get_id());
|
||||
}
|
||||
this.popdown();
|
||||
},
|
||||
|
177
js/ui/calendar.js
Normal file
177
js/ui/calendar.js
Normal file
@ -0,0 +1,177 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
function _sameDay(dateA, dateB) {
|
||||
return (dateA.getDate() == dateB.getDate() &&
|
||||
dateA.getMonth() == dateB.getMonth() &&
|
||||
dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
function Calendar() {
|
||||
this._init();
|
||||
};
|
||||
|
||||
Calendar.prototype = {
|
||||
_init: function() {
|
||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||
// should add a C function so we can do the full handling.
|
||||
this._weekStart = NaN;
|
||||
let weekStartString = Gettext_gtk20.gettext("calendar:week_start:0");
|
||||
if (weekStartString.indexOf("calendar:week_start:") == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
|
||||
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
|
||||
log("Translation of 'calendar:week_start:0' in GTK+ is not correct");
|
||||
this.weekStart = 0;
|
||||
}
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
switch (Gettext_gtk20.gettext("calendar:MY")) {
|
||||
case "calendar:MY":
|
||||
this._headerFormat = "%B %Y";
|
||||
break;
|
||||
case "calendar:YM":
|
||||
this._headerFormat = "%Y %B";
|
||||
break;
|
||||
default:
|
||||
log("Translation of 'calendar:MY' in GTK+ is not correct");
|
||||
this._headerFormat = "%B %Y";
|
||||
break;
|
||||
}
|
||||
|
||||
// Start off with the current date
|
||||
this.date = new Date();
|
||||
|
||||
this.actor = new St.Table({ homogeneous: false,
|
||||
style_class: "calendar",
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('scroll-event',
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
// Top line of the calendar '<| September 2009 |>'
|
||||
this._topBox = new St.BoxLayout();
|
||||
this.actor.add(this._topBox,
|
||||
{ row: 0, col: 0, col_span: 7 });
|
||||
|
||||
let back = new St.Button({ label: "<", style_class: 'calendar-change-month' });
|
||||
this._topBox.add(back);
|
||||
back.connect("clicked", Lang.bind(this, this._prevMonth));
|
||||
|
||||
this._dateLabel = new St.Label();
|
||||
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
|
||||
let forward = new St.Button({ label: ">", style_class: 'calendar-change-month' });
|
||||
this._topBox.add(forward);
|
||||
forward.connect("clicked", Lang.bind(this, this._nextMonth));
|
||||
|
||||
// We need to figure out the abbreviated localized names for the days of the week;
|
||||
// we do this by just getting the next 7 days starting from right now and then putting
|
||||
// them in the right cell in the table. It doesn't matter if we add them in order
|
||||
let iter = new Date(this.date);
|
||||
iter.setSeconds(0); // Leap second protection. Hah!
|
||||
iter.setHours(12);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
this.actor.add(new St.Label({ text: iter.toLocaleFormat("%a") }),
|
||||
{ row: 1,
|
||||
col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: 1.0 });
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
// All the children after this are days, and get removed when we update the calendar
|
||||
this._firstDayIndex = this.actor.get_children().length;
|
||||
|
||||
this._update();
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this.date)) {
|
||||
this.date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
_onScroll : function(actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
case Clutter.ScrollDirection.LEFT:
|
||||
this._prevMonth();
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
case Clutter.ScrollDirection.RIGHT:
|
||||
this._nextMonth();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_prevMonth: function() {
|
||||
if (this.date.getMonth() == 0) {
|
||||
this.date.setMonth(11);
|
||||
this.date.setFullYear(this.date.getFullYear() - 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() - 1);
|
||||
}
|
||||
this._update();
|
||||
},
|
||||
|
||||
_nextMonth: function() {
|
||||
if (this.date.getMonth() == 11) {
|
||||
this.date.setMonth(0);
|
||||
this.date.setFullYear(this.date.getFullYear() + 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() + 1);
|
||||
}
|
||||
this._update();
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
|
||||
|
||||
// Remove everything but the topBox and the weekday labels
|
||||
let children = this.actor.get_children();
|
||||
for (let i = this._firstDayIndex; i < children.length; i++)
|
||||
children[i].destroy();
|
||||
|
||||
// Start at the beginning of the week before the start of the month
|
||||
let iter = new Date(this.date);
|
||||
iter.setDate(1);
|
||||
iter.setSeconds(0);
|
||||
iter.setHours(12);
|
||||
iter.setTime(iter.getTime() - (iter.getDay() - this._weekStart) * MSECS_IN_DAY);
|
||||
|
||||
let now = new Date();
|
||||
|
||||
let row = 2;
|
||||
while (true) {
|
||||
let label = new St.Label({ text: iter.getDate().toString() });
|
||||
if (_sameDay(now, iter))
|
||||
label.style_class = "calendar-day calendar-today";
|
||||
else if (iter.getMonth() != this.date.getMonth())
|
||||
label.style_class = "calendar-day calendar-other-month-day";
|
||||
else
|
||||
label.style_class = "calendar-day";
|
||||
this.actor.add(label,
|
||||
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: 1.0 });
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
if (iter.getDay() == this._weekStart) {
|
||||
// We stop on the first "first day of the week" after the month we are displaying
|
||||
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
|
||||
break;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -889,6 +889,9 @@ Dash.prototype = {
|
||||
_updateDashActors: function() {
|
||||
if (this._searchPending) {
|
||||
this._searchResultsSection.actor.show();
|
||||
// We initially hide all sections when we start a search. When the search timeout
|
||||
// first runs, the sections that have matching results are shown. As the search
|
||||
// is refined, only the sections that have matching results will be shown.
|
||||
for (let i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.header.actor.hide();
|
||||
@ -897,13 +900,7 @@ Dash.prototype = {
|
||||
this._appsSection.actor.hide();
|
||||
this._placesSection.actor.hide();
|
||||
this._docsSection.actor.hide();
|
||||
} else if (this._searchActive) {
|
||||
for (let i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.header.actor.show();
|
||||
section.resultArea.actor.show();
|
||||
}
|
||||
} else {
|
||||
} else if (!this._searchActive) {
|
||||
this._showAllSearchSections();
|
||||
this._searchResultsSection.actor.hide();
|
||||
this._appsSection.actor.show();
|
||||
|
@ -136,7 +136,7 @@ DocDisplay.prototype = {
|
||||
// but redisplaying right away is cool when we use Zephyr.
|
||||
// Also, we might be displaying remote documents, like Google Docs, in the future
|
||||
// which might be edited by someone else.
|
||||
this._redisplay(false);
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, function (o) {
|
||||
|
36
js/ui/environment.js
Normal file
36
js/ui/environment.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Format = imports.misc.format;
|
||||
|
||||
// "monkey patch" in some varargs ClutterContainer methods; we need
|
||||
// to do this per-container class since there is no representation
|
||||
// of interfaces in Javascript
|
||||
function _patchContainerClass(containerClass) {
|
||||
// This one is a straightforward mapping of the C method
|
||||
containerClass.prototype.child_set = function(actor, props) {
|
||||
let meta = this.get_child_meta(actor);
|
||||
for (prop in props)
|
||||
meta[prop] = props[prop];
|
||||
};
|
||||
|
||||
// clutter_container_add() actually is a an add-many-actors
|
||||
// method. We conveniently, but somewhat dubiously, take the
|
||||
// this opportunity to make it do something more useful.
|
||||
containerClass.prototype.add = function(actor, props) {
|
||||
this.add_actor(actor);
|
||||
if (props)
|
||||
this.child_set(actor, props);
|
||||
};
|
||||
}
|
||||
|
||||
_patchContainerClass(St.BoxLayout);
|
||||
_patchContainerClass(St.Table);
|
||||
|
||||
function init() {
|
||||
Tweener.init();
|
||||
String.prototype.format = Format.format;
|
||||
}
|
@ -370,7 +370,11 @@ GenericDisplay.prototype = {
|
||||
return;
|
||||
let flags = RedisplayFlags.RESET_CONTROLS;
|
||||
if (this._search != '') {
|
||||
if (lowertext.indexOf(this._search) == 0)
|
||||
// Because we combine search terms with OR, we have to be sure that no new term
|
||||
// was introduced before deciding that the new search results will be a subset of
|
||||
// the existing search results.
|
||||
if (lowertext.indexOf(this._search) == 0 &&
|
||||
lowertext.split(/\s+/).length == this._search.split(/\s+/).length)
|
||||
flags |= RedisplayFlags.SUBSEARCH;
|
||||
}
|
||||
this._search = lowertext;
|
||||
@ -486,15 +490,16 @@ GenericDisplay.prototype = {
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
_redisplayFull: function() {
|
||||
_recreateDisplayItems: function() {
|
||||
this._removeAllDisplayItems();
|
||||
this._setDefaultList();
|
||||
for (let itemId in this._allItems) {
|
||||
this._addDisplayItem(itemId);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates a display item based on the information associated with itemId
|
||||
// and adds it to the displayed items.
|
||||
// and adds it to the list of displayed items, but does not yet display it.
|
||||
_addDisplayItem : function(itemId) {
|
||||
if (this._displayedItems.hasOwnProperty(itemId)) {
|
||||
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
|
||||
@ -525,7 +530,6 @@ GenericDisplay.prototype = {
|
||||
this.emit('show-details', index);
|
||||
}
|
||||
}));
|
||||
this._list.add_actor(displayItem.actor);
|
||||
this._displayedItems[itemId] = displayItem;
|
||||
},
|
||||
|
||||
@ -635,6 +639,7 @@ GenericDisplay.prototype = {
|
||||
* their own while the user was browsing through the result pages.
|
||||
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
||||
* one, which implies we only need to re-search through previous results.
|
||||
* FULL - Indicates that we need refresh all displayed items.
|
||||
*/
|
||||
_redisplay: function(flags) {
|
||||
let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0;
|
||||
@ -642,13 +647,20 @@ GenericDisplay.prototype = {
|
||||
let fullReload = (flags & RedisplayFlags.FULL) > 0;
|
||||
|
||||
let hadSelected = this.hasSelected();
|
||||
this.unsetSelected();
|
||||
|
||||
if (!this._initialLoadComplete || !this._refreshCache())
|
||||
if (!this._initialLoadComplete)
|
||||
fullReload = true;
|
||||
|
||||
if (!this._refreshCache())
|
||||
fullReload = true;
|
||||
|
||||
if (fullReload) {
|
||||
this._recreateDisplayItems();
|
||||
this._initialLoadComplete = true;
|
||||
this._redisplayFull();
|
||||
} if (isSubSearch) {
|
||||
}
|
||||
|
||||
if (isSubSearch) {
|
||||
this._redisplaySubSearch();
|
||||
} else {
|
||||
this._redisplayReordering();
|
||||
|
@ -4,6 +4,7 @@ const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
@ -12,18 +13,7 @@ const Mainloop = imports.mainloop;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const LG_BORDER_COLOR = new Clutter.Color();
|
||||
LG_BORDER_COLOR.from_pixel(0x0000aca0);
|
||||
const LG_BACKGROUND_COLOR = new Clutter.Color();
|
||||
LG_BACKGROUND_COLOR.from_pixel(0x000000d5);
|
||||
const GREY = new Clutter.Color();
|
||||
GREY.from_pixel(0xAFAFAFFF);
|
||||
const MATRIX_GREEN = new Clutter.Color();
|
||||
MATRIX_GREEN.from_pixel(0x88ff66ff);
|
||||
// FIXME pull from GConf
|
||||
const MATRIX_FONT = 'Monospace 10';
|
||||
|
||||
/* Imports...feel free to add here as needed */
|
||||
/* Imports...feel free to add here as needed */
|
||||
var commandHeader = "const Clutter = imports.gi.Clutter; " +
|
||||
"const GLib = imports.gi.GLib; " +
|
||||
"const Gtk = imports.gi.Gtk; " +
|
||||
@ -47,7 +37,7 @@ function Notebook() {
|
||||
|
||||
Notebook.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Big.Box();
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
|
||||
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4, padding: 2 });
|
||||
@ -58,21 +48,24 @@ Notebook.prototype = {
|
||||
|
||||
appendPage: function(name, child) {
|
||||
let labelOuterBox = new Big.Box({ padding: 2 });
|
||||
let labelBox = new Big.Box({ padding: 2, border_color: MATRIX_GREEN,
|
||||
reactive: true });
|
||||
let labelBox = new St.BoxLayout({ reactive: true });
|
||||
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
|
||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
text: name });
|
||||
let label = new St.Label({ text: name });
|
||||
labelBox.connect('button-press-event', Lang.bind(this, function () {
|
||||
this.selectChild(child);
|
||||
return true;
|
||||
}));
|
||||
labelBox.append(label, Big.BoxPackFlags.EXPAND);
|
||||
this._tabs.push([child, labelBox]);
|
||||
child.hide();
|
||||
this.actor.append(child, Big.BoxPackFlags.EXPAND);
|
||||
labelBox.add(label, { expand: true });
|
||||
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||
scrollview.get_hscroll_bar().hide();
|
||||
scrollview.add_actor(child);
|
||||
|
||||
this._tabs.push([child, labelBox, scrollview]);
|
||||
scrollview.hide();
|
||||
this.actor.add(scrollview, { expand: true });
|
||||
|
||||
if (this._selectedIndex == -1)
|
||||
this.selectIndex(0);
|
||||
},
|
||||
@ -80,10 +73,10 @@ Notebook.prototype = {
|
||||
_unselect: function() {
|
||||
if (this._selectedIndex < 0)
|
||||
return;
|
||||
let [child, labelBox] = this._tabs[this._selectedIndex];
|
||||
let [child, labelBox, scrollview] = this._tabs[this._selectedIndex];
|
||||
labelBox.padding = 2;
|
||||
labelBox.border = 0;
|
||||
child.hide();
|
||||
scrollview.hide();
|
||||
this._selectedIndex = -1;
|
||||
},
|
||||
|
||||
@ -95,10 +88,10 @@ Notebook.prototype = {
|
||||
this.emit('selection', null);
|
||||
return;
|
||||
}
|
||||
let [child, labelBox] = this._tabs[index];
|
||||
let [child, labelBox, scrollview] = this._tabs[index];
|
||||
labelBox.padding = 1;
|
||||
labelBox.border = 1;
|
||||
child.show();
|
||||
scrollview.show();
|
||||
this._selectedIndex = index;
|
||||
this.emit('selection', child);
|
||||
},
|
||||
@ -108,7 +101,7 @@ Notebook.prototype = {
|
||||
this.selectIndex(-1);
|
||||
else {
|
||||
for (let i = 0; i < this._tabs.length; i++) {
|
||||
let [tabChild, labelBox] = this._tabs[i];
|
||||
let [tabChild, labelBox, scrollview] = this._tabs[i];
|
||||
if (tabChild == child) {
|
||||
this.selectIndex(i);
|
||||
return;
|
||||
@ -130,20 +123,19 @@ Result.prototype = {
|
||||
|
||||
this.actor = new Big.Box();
|
||||
|
||||
let cmdTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: command });
|
||||
let cmdTxt = new St.Label({ text: command });
|
||||
cmdTxt.ellipsize = Pango.EllipsizeMode.END;
|
||||
|
||||
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
|
||||
let resultTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: "r(" + index + ") = " + o });
|
||||
let resultTxt = new St.Label({ text: "r(" + index + ") = " + o });
|
||||
resultTxt.ellipsize = Pango.EllipsizeMode.END;
|
||||
|
||||
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
|
||||
let line = new Big.Box({ border_color: GREY,
|
||||
border_bottom: 1,
|
||||
height: 8 });
|
||||
this.actor.append(line, Big.BoxPackFlags.NONE);
|
||||
let line = new Clutter.Rectangle({ name: "Separator",
|
||||
height: 1 });
|
||||
let padBin = new St.Bin({ name: "Separator", x_fill: true, y_fill: true });
|
||||
padBin.add_actor(line);
|
||||
this.actor.append(padBin, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,17 +150,14 @@ ActorHierarchy.prototype = {
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new Big.Box({ spacing: 4,
|
||||
border: 1,
|
||||
padding: 4,
|
||||
border_color: GREY });
|
||||
this.actor = new St.BoxLayout({ name: "ActorHierarchy", vertical: true });
|
||||
},
|
||||
|
||||
setTarget: function(actor) {
|
||||
this._previousTarget = this._target;
|
||||
this.target = actor;
|
||||
|
||||
this.actor.remove_all();
|
||||
this.actor.get_children().forEach(function (child) { child.destroy(); });
|
||||
|
||||
if (!(actor instanceof Clutter.Actor))
|
||||
return;
|
||||
@ -181,11 +170,9 @@ ActorHierarchy.prototype = {
|
||||
while ((parent = parent.get_parent()) != null) {
|
||||
this._parentList.push(parent);
|
||||
|
||||
let link = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
reactive: true,
|
||||
text: "" + parent });
|
||||
this.actor.append(link, Big.BoxPackFlags.IF_FITS);
|
||||
let link = new St.Label({ reactive: true,
|
||||
text: "" + parent });
|
||||
this.actor.add_actor(link);
|
||||
let parentTarget = parent;
|
||||
link.connect('button-press-event', Lang.bind(this, function () {
|
||||
this._selectByActor(parentTarget);
|
||||
@ -214,16 +201,13 @@ PropertyInspector.prototype = {
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new Big.Box({ spacing: 4,
|
||||
border: 1,
|
||||
padding: 4,
|
||||
border_color: GREY });
|
||||
this.actor = new St.BoxLayout({ name: "PropertyInspector", vertical: true });
|
||||
},
|
||||
|
||||
setTarget: function(actor) {
|
||||
this.target = actor;
|
||||
|
||||
this.actor.remove_all();
|
||||
this.actor.get_children().forEach(function (child) { child.destroy(); });
|
||||
|
||||
for (let propName in actor) {
|
||||
let valueStr;
|
||||
@ -233,11 +217,9 @@ PropertyInspector.prototype = {
|
||||
valueStr = '<error>';
|
||||
}
|
||||
let propText = propName + ": " + valueStr;
|
||||
let propDisplay = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
reactive: true,
|
||||
text: propText });
|
||||
this.actor.append(propDisplay, Big.BoxPackFlags.IF_FITS);
|
||||
let propDisplay = new St.Label({ reactive: true,
|
||||
text: propText });
|
||||
this.actor.add_actor(propDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,20 +231,17 @@ function Inspector() {
|
||||
Inspector.prototype = {
|
||||
_init: function() {
|
||||
let width = 150;
|
||||
let eventHandler = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
||||
border: 1,
|
||||
border_color: LG_BORDER_COLOR,
|
||||
corner_radius: 4,
|
||||
y: global.stage.height/2,
|
||||
reactive: true
|
||||
});
|
||||
let primary = global.get_primary_monitor();
|
||||
let eventHandler = new St.BoxLayout({ name: "LookingGlassDialog",
|
||||
vertical: false,
|
||||
y: primary.y + Math.floor(primary.height / 2),
|
||||
reactive: true });
|
||||
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
|
||||
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
|
||||
eventHandler.x = primary.x + Math.floor((primary.width - eventHandler.width) / 2);
|
||||
}));
|
||||
global.stage.add_actor(eventHandler);
|
||||
let displayText = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT, text: '' });
|
||||
eventHandler.append(displayText, Big.BoxPackFlags.EXPAND);
|
||||
let displayText = new St.Label();
|
||||
eventHandler.add(displayText, { expand: true });
|
||||
|
||||
let borderPaintTarget = null;
|
||||
let borderPaintId = null;
|
||||
@ -321,29 +300,26 @@ LookingGlass.prototype = {
|
||||
this._offset = 0;
|
||||
this._results = [];
|
||||
|
||||
// TODO replace with scrolling or something better
|
||||
this._maxItems = 10;
|
||||
// Sort of magic, but...eh.
|
||||
this._maxItems = 150;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: "LookingGlassDialog",
|
||||
vertical: true,
|
||||
visible: false });
|
||||
|
||||
let gconf = Shell.GConf.get_default();
|
||||
gconf.watch_directory("/desktop/gnome/interface");
|
||||
gconf.connect("changed::/desktop/gnome/interface/monospace_font_name",
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._updateFont();
|
||||
|
||||
this.actor = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
||||
border: 1,
|
||||
border_color: LG_BORDER_COLOR,
|
||||
corner_radius: 4,
|
||||
padding_top: 8,
|
||||
padding_left: 4,
|
||||
padding_right: 4,
|
||||
padding_bottom: 4,
|
||||
spacing: 4,
|
||||
visible: false
|
||||
});
|
||||
global.stage.add_actor(this.actor);
|
||||
|
||||
let toolbar = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
border: 1, border_color: GREY,
|
||||
corner_radius: 4 });
|
||||
this.actor.append(toolbar, Big.BoxPackFlags.NONE);
|
||||
let toolbar = new St.BoxLayout({ name: "Toolbar" });
|
||||
this.actor.add_actor(toolbar);
|
||||
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
||||
24);
|
||||
toolbar.append(inspectIcon, Big.BoxPackFlags.NONE);
|
||||
toolbar.add_actor(inspectIcon);
|
||||
inspectIcon.reactive = true;
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector();
|
||||
@ -361,31 +337,26 @@ LookingGlass.prototype = {
|
||||
}));
|
||||
|
||||
let notebook = new Notebook();
|
||||
this.actor.append(notebook.actor, Big.BoxPackFlags.EXPAND);
|
||||
toolbar.append(notebook.tabControls, Big.BoxPackFlags.END);
|
||||
this.actor.add(notebook.actor, { expand: true });
|
||||
|
||||
this._evalBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: 4 });
|
||||
let emptyBox = new St.Bin();
|
||||
toolbar.add(emptyBox, { expand: true });
|
||||
toolbar.add_actor(notebook.tabControls);
|
||||
|
||||
this._evalBox = new St.BoxLayout({ name: "EvalBox", vertical: true });
|
||||
notebook.appendPage('Evaluator', this._evalBox);
|
||||
|
||||
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: 4 });
|
||||
this._evalBox.append(this._resultsArea, Big.BoxPackFlags.EXPAND);
|
||||
this._evalBox.add(this._resultsArea, { expand: true });
|
||||
|
||||
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
this._evalBox.append(entryArea, Big.BoxPackFlags.NONE);
|
||||
this._evalBox.add_actor(entryArea);
|
||||
|
||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
text: 'js>>> ' });
|
||||
let label = new St.Label({ text: 'js>>> ' });
|
||||
entryArea.append(label, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._entry = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
editable: true,
|
||||
activatable: true,
|
||||
singleLineMode: true,
|
||||
text: ''});
|
||||
this._entry = new St.Entry();
|
||||
/* unmapping the edit box will un-focus it, undo that */
|
||||
notebook.connect('selection', Lang.bind(this, function (nb, child) {
|
||||
if (child == this._evalBox)
|
||||
@ -403,7 +374,7 @@ LookingGlass.prototype = {
|
||||
notebook.selectIndex(0);
|
||||
}));
|
||||
|
||||
this._entry.connect('activate', Lang.bind(this, function (o, e) {
|
||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
||||
let text = o.get_text();
|
||||
// Ensure we don't get newlines in the command; the history file is
|
||||
// newline-separated.
|
||||
@ -416,7 +387,7 @@ LookingGlass.prototype = {
|
||||
this._historyNavIndex = -1;
|
||||
return true;
|
||||
}));
|
||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
@ -446,6 +417,19 @@ LookingGlass.prototype = {
|
||||
}));
|
||||
},
|
||||
|
||||
_updateFont: function() {
|
||||
let gconf = Shell.GConf.get_default();
|
||||
let fontName = gconf.get_string("/desktop/gnome/interface/monospace_font_name");
|
||||
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
||||
let fontDesc = Pango.Font.description_from_string(fontName);
|
||||
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
||||
// monospace font to be bold/oblique/etc. Could easily be added here.
|
||||
this.actor.style =
|
||||
'font-size: ' + fontDesc.get_size() / 1024. + (fontDesc.get_size_is_absolute() ? 'px' : 'pt') + ';'
|
||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||
},
|
||||
|
||||
_readHistory: function () {
|
||||
if (!this._historyFile.query_exists(null))
|
||||
return;
|
||||
@ -518,11 +502,11 @@ LookingGlass.prototype = {
|
||||
},
|
||||
|
||||
_resizeTo: function(actor) {
|
||||
let stage = global.stage;
|
||||
let myWidth = stage.width * 0.7;
|
||||
let myHeight = stage.height * 0.7;
|
||||
let primary = global.get_primary_monitor();
|
||||
let myWidth = primary.width * 0.7;
|
||||
let myHeight = primary.height * 0.7;
|
||||
let [srcX, srcY] = actor.get_transformed_position();
|
||||
this.actor.x = srcX + (stage.width-myWidth)/2;
|
||||
this.actor.x = srcX + (primary.width - myWidth) / 2;
|
||||
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
|
||||
this._targetY = this._hiddenY + myHeight;
|
||||
this.actor.y = this._hiddenY;
|
||||
|
@ -10,15 +10,16 @@ const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Chrome = imports.ui.chrome;
|
||||
const Environment = imports.ui.environment;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const Sidebar = imports.ui.sidebar;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
@ -52,7 +53,7 @@ function start() {
|
||||
// back into sync ones.
|
||||
DBus.session.flush();
|
||||
|
||||
Tweener.init();
|
||||
Environment.init();
|
||||
|
||||
// Ensure ShellAppMonitor is initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
@ -75,6 +76,11 @@ function start() {
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].destroy();
|
||||
|
||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||
let stylesheetPath = global.datadir + "/theme/gnome-shell.css";
|
||||
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
|
||||
themeContext.set_theme (theme);
|
||||
|
||||
global.connect('panel-run-dialog', function(panel) {
|
||||
// Make sure not more than one run dialog is shown.
|
||||
getRunDialog().open();
|
||||
@ -121,7 +127,9 @@ function start() {
|
||||
}
|
||||
|
||||
function _relayout() {
|
||||
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
|
||||
let primary = global.get_primary_monitor();
|
||||
panel.actor.set_position(primary.x, primary.y);
|
||||
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
|
||||
overview.relayout();
|
||||
}
|
||||
|
||||
@ -193,7 +201,7 @@ function _globalKeyPressHandler(actor, event) {
|
||||
overview.hide();
|
||||
|
||||
return true;
|
||||
} else if (symbol == Clutter.F2 && (event.get_state() & Clutter.ModifierType.MOD1_MASK)) {
|
||||
} else if (symbol == Clutter.F2 && (Shell.get_event_state(event) & Clutter.ModifierType.MOD1_MASK)) {
|
||||
getRunDialog().open();
|
||||
}
|
||||
}
|
||||
|
@ -142,42 +142,44 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
_recalculateGridSizes: function () {
|
||||
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
||||
(global.screen_height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
||||
let primary = global.get_primary_monitor();
|
||||
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
||||
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
||||
|
||||
// We divide the screen into an imaginary grid which helps us determine the layout of
|
||||
// different visual components.
|
||||
if (wideScreen) {
|
||||
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
|
||||
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
|
||||
displayGridColumnWidth = primary.width / COLUMNS_WIDE_SCREEN;
|
||||
displayGridRowHeight = primary.height / ROWS_WIDE_SCREEN;
|
||||
} else {
|
||||
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
|
||||
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
|
||||
displayGridColumnWidth = primary.width / COLUMNS_REGULAR_SCREEN;
|
||||
displayGridRowHeight = primary.height / ROWS_REGULAR_SCREEN;
|
||||
}
|
||||
},
|
||||
|
||||
relayout: function () {
|
||||
let screenHeight = global.screen_height;
|
||||
let screenWidth = global.screen_width;
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
|
||||
let contentY = Panel.PANEL_HEIGHT;
|
||||
let contentHeight = screenHeight - contentY;
|
||||
let contentHeight = primary.height - contentY;
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(screenWidth, contentHeight);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
|
||||
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||
|
||||
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
|
||||
- WORKSPACE_GRID_PADDING * 2;
|
||||
// We scale the vertical padding by (screenHeight / screenWidth)
|
||||
// We scale the vertical padding by (primary.height / primary.width)
|
||||
// so that the workspace preserves its aspect ratio.
|
||||
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
|
||||
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
|
||||
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2;
|
||||
|
||||
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
|
||||
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
|
||||
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width);
|
||||
|
||||
this._dash.actor.set_position(0, contentY);
|
||||
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
|
||||
@ -187,10 +189,12 @@ Overview.prototype = {
|
||||
// place the 'Add Workspace' button in the bottom row of the grid
|
||||
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
|
||||
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
|
||||
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
|
||||
this._addButtonY = primary.height - Math.floor(displayGridRowHeight * 4/5);
|
||||
|
||||
this._backOver.set_position(0, contentY);
|
||||
this._backOver.set_size(global.screen_width, contentHeight);
|
||||
// The parent (this._group) is positioned at the top left of the primary monitor
|
||||
// while this._backOver occupies the entire screen.
|
||||
this._backOver.set_position(- primary.x, - primary.y);
|
||||
this._backOver.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
|
||||
contentY);
|
||||
@ -198,7 +202,7 @@ Overview.prototype = {
|
||||
this._paneContainer.height = contentHeight;
|
||||
|
||||
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
|
||||
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
|
||||
this._transparentBackground.set_size(primary.width - this._paneContainer.x,
|
||||
this._paneContainer.height);
|
||||
|
||||
if (this._activeDisplayPane != null)
|
||||
@ -322,9 +326,10 @@ Overview.prototype = {
|
||||
// The opposite transition is used in hide().
|
||||
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
|
||||
[this._group.x, this._group.y] = this.getZoomedInPosition();
|
||||
let primary = global.get_primary_monitor();
|
||||
Tweener.addTween(this._group,
|
||||
{ x: 0,
|
||||
y: 0,
|
||||
{ x: primary.x,
|
||||
y: primary.y,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
transition: 'easeOutQuad',
|
||||
@ -355,6 +360,10 @@ Overview.prototype = {
|
||||
this._activeDisplayPane.close();
|
||||
this._workspaces.hide();
|
||||
|
||||
this._addButton.actor.destroy();
|
||||
this._addButton.actor = null;
|
||||
this._addButton = null;
|
||||
|
||||
// Create a zoom in effect by transforming the Overview group so that
|
||||
// the active workspace fills up the whole screen. The opposite
|
||||
// transition is used in show().
|
||||
|
@ -7,12 +7,14 @@ const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Button = imports.ui.button;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const PANEL_HEIGHT = 26;
|
||||
@ -53,12 +55,13 @@ const TRAY_SPACING_MIN = 8;
|
||||
// Used for the tray icon container with gtk pre-2.16, which doesn't
|
||||
// fully support tray icon transparency
|
||||
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
|
||||
TRAY_BACKGROUND_COLOR.from_pixel(0xefefefff);
|
||||
TRAY_BACKGROUND_COLOR.from_pixel(0x0b0b0bff);
|
||||
const TRAY_BORDER_COLOR = new Clutter.Color();
|
||||
TRAY_BORDER_COLOR.from_pixel(0x00000033);
|
||||
const TRAY_CORNER_RADIUS = 5;
|
||||
const TRAY_BORDER_WIDTH = 0;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
|
||||
function AppPanelMenu() {
|
||||
this._init();
|
||||
@ -288,6 +291,8 @@ Panel.prototype = {
|
||||
opacity: 0,
|
||||
reactive: true });
|
||||
|
||||
this._hotCornerActivationTime = 0;
|
||||
|
||||
this._hotCornerEnvirons.connect('leave-event',
|
||||
Lang.bind(this, this._onHotCornerEnvironsLeft));
|
||||
// Clicking on the hot corner environs should result in the same bahavior
|
||||
@ -313,10 +318,17 @@ Panel.prototype = {
|
||||
|
||||
/* center */
|
||||
|
||||
let clockButton = new St.Button({ style_class: "panel-button",
|
||||
toggle_mode: true });
|
||||
this._centerBox.append(clockButton, Big.BoxPackFlags.NONE);
|
||||
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
|
||||
|
||||
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
|
||||
color: PANEL_FOREGROUND_COLOR,
|
||||
text: "" });
|
||||
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
|
||||
clockButton.add_actor(this._clock);
|
||||
|
||||
this._calendarPopup = null;
|
||||
|
||||
/* right */
|
||||
|
||||
@ -395,14 +407,14 @@ Panel.prototype = {
|
||||
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
|
||||
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
||||
// to switch to.
|
||||
this.button.actor.connect('button-press-event', function(b, e) {
|
||||
if (e.get_button() == 1 && e.get_click_count() == 1) {
|
||||
Main.overview.toggle();
|
||||
this.button.actor.connect('button-press-event', Lang.bind(this, function(b, e) {
|
||||
if (e.get_button() == 1 && e.get_click_count() == 1 && !Main.overview.animationInProgress) {
|
||||
this._maybeToggleOverviewOnClick();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}));
|
||||
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
|
||||
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
|
||||
// and to be released when it is exited regardless of how it was triggered.
|
||||
@ -454,10 +466,21 @@ Panel.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_toggleCalendar: function(clockButton) {
|
||||
if (clockButton.checked) {
|
||||
if (this._calendarPopup == null)
|
||||
this._calendarPopup = new CalendarPopup();
|
||||
this._calendarPopup.show();
|
||||
} else {
|
||||
this._calendarPopup.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onHotCornerEntered : function() {
|
||||
if (!this._hotCornerEntered) {
|
||||
this._hotCornerEntered = true;
|
||||
if (!Main.overview.animationInProgress) {
|
||||
this._hotCornerActivationTime = Date.now() / 1000;
|
||||
Main.overview.toggle();
|
||||
}
|
||||
}
|
||||
@ -466,7 +489,7 @@ Panel.prototype = {
|
||||
|
||||
_onHotCornerClicked : function() {
|
||||
if (!Main.overview.animationInProgress) {
|
||||
Main.overview.toggle();
|
||||
this._maybeToggleOverviewOnClick();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -483,5 +506,65 @@ Panel.prototype = {
|
||||
this._hotCornerEntered = false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
|
||||
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
|
||||
// and clicked the Activities button.
|
||||
_maybeToggleOverviewOnClick: function() {
|
||||
if (this._hotCornerActivationTime == 0 || Date.now() / 1000 - this._hotCornerActivationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
||||
Main.overview.toggle();
|
||||
this._hotCornerActivationTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function CalendarPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CalendarPopup.prototype = {
|
||||
_init: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'calendarPopup' });
|
||||
|
||||
this.calendar = new Calendar.Calendar();
|
||||
this.actor.add(this.calendar.actor);
|
||||
|
||||
// Directly adding the actor to Main.chrome.actor is a hack to
|
||||
// work around the fact that there is no way to add an actor that
|
||||
// affects the input region but not the shape.
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=597044
|
||||
Main.chrome.actor.add_actor(this.actor);
|
||||
Main.chrome.addInputRegionActor(this.actor);
|
||||
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
// Reset the calendar to today's date
|
||||
this.calendar.setDate(new Date());
|
||||
|
||||
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
|
||||
this.actor.lower(panelActor);
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: panelActor.y + panelActor.height,
|
||||
time: 0.2,
|
||||
transition: "easeOutQuad"
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: panelActor.y + panelActor.height - this.actor.height,
|
||||
time: 0.2,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: function() { this.actor.hide(); },
|
||||
onCompleteScope: this
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -9,11 +9,16 @@ const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
|
||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||
|
||||
const PLACES_VSPACING = 8;
|
||||
const PLACES_ICON_SIZE = 16;
|
||||
|
||||
@ -75,17 +80,33 @@ function Places() {
|
||||
|
||||
Places.prototype = {
|
||||
_init : function() {
|
||||
// Places is divided semi-arbitrarily into left and right; a grid would
|
||||
// look better in that there would be an even number of items left+right,
|
||||
// but it seems like we want some sort of differentiation between actions
|
||||
// like "Connect to server..." and regular folders
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4 });
|
||||
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
|
||||
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
|
||||
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// Subdivide left into actions and devices
|
||||
this._actionsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
|
||||
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING,
|
||||
padding_top: 6 });
|
||||
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
// Right is bookmarks
|
||||
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
|
||||
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
let gconf = Shell.GConf.get_default();
|
||||
gconf.watch_directory(NAUTILUS_PREFS_DIR);
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
@ -99,13 +120,27 @@ Places.prototype = {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
||||
});
|
||||
|
||||
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(home.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
let desktopFile = Gio.file_new_for_path (desktopPath);
|
||||
let desktopUri = desktopFile.get_uri();
|
||||
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
||||
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
||||
this._desktopMenu = new PlaceDisplay(desktopLabel,
|
||||
function() {
|
||||
return Shell.TextureCache.get_default().load_gicon(desktopIcon, PLACES_ICON_SIZE);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext());
|
||||
});
|
||||
this._actionsBox.append(this._desktopMenu.actor, Big.BoxPackFlags.NONE);
|
||||
this._updateDesktopMenuVisibility();
|
||||
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
||||
|
||||
/*
|
||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||
*/
|
||||
|
||||
this._menuBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
||||
@ -137,17 +172,17 @@ Places.prototype = {
|
||||
function () {
|
||||
networkApp.launch();
|
||||
});
|
||||
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(network.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
let connect = new PlaceDisplay('Connect to...',
|
||||
let connect = new PlaceDisplay(_("Connect to..."),
|
||||
function () {
|
||||
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
|
||||
},
|
||||
function () {
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
});
|
||||
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
@ -272,7 +307,15 @@ Places.prototype = {
|
||||
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
|
||||
});
|
||||
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
},
|
||||
|
||||
_updateDesktopMenuVisibility: function() {
|
||||
let gconf = Shell.GConf.get_default();
|
||||
let desktopIsHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
||||
if (desktopIsHome)
|
||||
this._desktopMenu.actor.hide();
|
||||
else
|
||||
this._desktopMenu.actor.show();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Places.prototype);
|
||||
|
@ -60,24 +60,26 @@ RunDialog.prototype = {
|
||||
|
||||
// All actors are inside _group. We create it initially
|
||||
// hidden then show it in show()
|
||||
this._group = new Clutter.Group({ visible: false });
|
||||
this._group = new Clutter.Group({ visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
global.stage.add_actor(this._group);
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group);
|
||||
let lightbox = new Lightbox.Lightbox(this._group);
|
||||
|
||||
let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
this._boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
this._group.add_actor(boxH);
|
||||
this._lightbox.highlight(boxH);
|
||||
this._group.add_actor(this._boxH);
|
||||
lightbox.highlight(this._boxH);
|
||||
|
||||
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
boxH.append(boxV, Big.BoxPackFlags.NONE);
|
||||
this._boxH.append(boxV, Big.BoxPackFlags.NONE);
|
||||
|
||||
|
||||
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
@ -87,7 +89,7 @@ RunDialog.prototype = {
|
||||
padding: DIALOG_PADDING,
|
||||
width: DIALOG_WIDTH });
|
||||
|
||||
boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
||||
this._boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
|
||||
font_name: '18px Sans',
|
||||
@ -146,6 +148,7 @@ RunDialog.prototype = {
|
||||
},
|
||||
|
||||
_run : function(command) {
|
||||
this._commandError = false;
|
||||
let f;
|
||||
if (this._enableInternalCommands)
|
||||
f = this._internalCommands[command];
|
||||
@ -155,7 +158,6 @@ RunDialog.prototype = {
|
||||
f();
|
||||
} else if (command) {
|
||||
try {
|
||||
this._commandError = false;
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
@ -168,7 +170,7 @@ RunDialog.prototype = {
|
||||
* We are only interested in the actual error, so parse that out.
|
||||
*/
|
||||
let m = /.+\((.+)\)/.exec(e);
|
||||
let errorStr = "Execution of '" + command + "' failed:\n" + m[1];
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + "\n" + m[1];
|
||||
this._errorMessage.set_text(errorStr);
|
||||
this._errorBox.show();
|
||||
}
|
||||
@ -182,6 +184,12 @@ RunDialog.prototype = {
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
|
||||
// Position the dialog on the current monitor
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._boxH.set_position(monitor.x, monitor.y);
|
||||
this._boxH.set_size(monitor.width, monitor.height);
|
||||
|
||||
this._isOpen = true;
|
||||
this._group.show();
|
||||
|
||||
|
@ -274,7 +274,7 @@ WindowManager.prototype = {
|
||||
_startAppSwitcher : function(shellwm, binding, window, backwards) {
|
||||
let tabPopup = new AltTab.AltTabPopup();
|
||||
|
||||
if (!tabPopup.show(backwards ? -1 : 1))
|
||||
if (!tabPopup.show(backwards))
|
||||
tabPopup.destroy();
|
||||
}
|
||||
};
|
||||
|
@ -111,6 +111,8 @@ WindowClone.prototype = {
|
||||
this.origX = realWindow.x;
|
||||
this.origY = realWindow.y;
|
||||
|
||||
this._stackAbove = null;
|
||||
|
||||
this._title = null;
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
@ -129,6 +131,8 @@ WindowClone.prototype = {
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this._inDrag = false;
|
||||
|
||||
this._zooming = false;
|
||||
},
|
||||
|
||||
setVisibleWithChrome: function(visible) {
|
||||
@ -143,6 +147,14 @@ WindowClone.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this._inDrag || this._zooming)
|
||||
// We'll fix up the stack after the drag/zooming
|
||||
return;
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
if (this._title)
|
||||
@ -157,7 +169,6 @@ WindowClone.prototype = {
|
||||
|
||||
this._havePointer = true;
|
||||
|
||||
actor.raise_top();
|
||||
this._updateTitle();
|
||||
},
|
||||
|
||||
@ -206,6 +217,8 @@ WindowClone.prototype = {
|
||||
},
|
||||
|
||||
_zoomStart : function () {
|
||||
this._zooming = true;
|
||||
|
||||
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
|
||||
|
||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||
@ -232,7 +245,10 @@ WindowClone.prototype = {
|
||||
},
|
||||
|
||||
_zoomEnd : function () {
|
||||
this._zooming = false;
|
||||
|
||||
this.actor.reparent(this._origParent);
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
|
||||
@ -272,6 +288,12 @@ WindowClone.prototype = {
|
||||
// mysteriously present
|
||||
this._havePointer = false;
|
||||
|
||||
// We may not have a parent if DnD completed successfully, in
|
||||
// which case our clone will shortly be destroyed and replaced
|
||||
// with a new one on the target workspace.
|
||||
if (this.actor.get_parent() != null)
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
@ -283,7 +305,6 @@ WindowClone.prototype = {
|
||||
// Called by Tweener
|
||||
onAnimationComplete : function () {
|
||||
this._updateTitle();
|
||||
this.actor.raise(this.stackAbove);
|
||||
},
|
||||
|
||||
_createTitle : function () {
|
||||
@ -618,12 +639,43 @@ Workspace.prototype = {
|
||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||
},
|
||||
|
||||
_isCloneVisible: function(clone) {
|
||||
return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows);
|
||||
},
|
||||
|
||||
/**
|
||||
* _getVisibleClones:
|
||||
*
|
||||
* Returns a list WindowClone objects where the clone isn't filtered
|
||||
* out by any application filter. The clone for the desktop is excluded.
|
||||
* The returned array will always be newly allocated; it is not in any
|
||||
* defined order, and thus it's convenient to call .sort() with your
|
||||
* choice of sorting function.
|
||||
*/
|
||||
_getVisibleClones: function() {
|
||||
let visible = [];
|
||||
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
|
||||
if (!this._isCloneVisible(clone))
|
||||
continue;
|
||||
|
||||
visible.push(clone);
|
||||
}
|
||||
return visible;
|
||||
},
|
||||
|
||||
_getVisibleWindows: function() {
|
||||
return this._getVisibleClones().map(function (clone) { return clone.metaWindow; });
|
||||
},
|
||||
|
||||
_resetCloneVisibility: function () {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let icon = this._windowIcons[i];
|
||||
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) {
|
||||
if (!this._isCloneVisible(clone)) {
|
||||
clone.setVisibleWithChrome(false);
|
||||
icon.hide();
|
||||
} else {
|
||||
@ -853,21 +905,12 @@ Workspace.prototype = {
|
||||
positionWindows : function(workspaceZooming) {
|
||||
let totalVisible = 0;
|
||||
|
||||
let visibleWindows = [];
|
||||
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
||||
continue;
|
||||
|
||||
visibleWindows.push(clone.metaWindow);
|
||||
}
|
||||
let visibleWindows = this._getVisibleWindows();
|
||||
|
||||
// Start the animations
|
||||
let slots = this._computeAllWindowSlots(visibleWindows.length);
|
||||
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
|
||||
|
||||
let previousWindow = this._windows[0];
|
||||
for (let i = 0; i < visibleWindows.length; i++) {
|
||||
let slot = slots[i];
|
||||
let metaWindow = visibleWindows[i];
|
||||
@ -875,9 +918,6 @@ Workspace.prototype = {
|
||||
let clone = metaWindow._delegate;
|
||||
let icon = this._windowIcons[mainIndex];
|
||||
|
||||
clone.stackAbove = previousWindow.actor;
|
||||
previousWindow = clone;
|
||||
|
||||
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
|
||||
|
||||
icon.hide();
|
||||
@ -896,6 +936,24 @@ Workspace.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
let desktopClone = this._windows[0];
|
||||
|
||||
let visibleClones = this._getVisibleClones();
|
||||
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||
|
||||
for (let i = 0; i < visibleClones.length; i++) {
|
||||
let clone = visibleClones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(desktopClone.actor);
|
||||
} else {
|
||||
let previousClone = visibleClones[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInWindowIcon: function (clone, icon) {
|
||||
icon.opacity = 0;
|
||||
icon.show();
|
||||
@ -1320,10 +1378,12 @@ Workspaces.prototype = {
|
||||
// workspaces have been created. This cannot be done first because
|
||||
// window movement depends on the Workspaces object being accessible
|
||||
// as an Overview member.
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
this._overviewShowingId =
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, function() {
|
||||
this._onRestacked();
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
}));
|
||||
|
||||
// Track changes to the number of workspaces
|
||||
@ -1333,6 +1393,9 @@ Workspaces.prototype = {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
this._restackedNotifyId =
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
},
|
||||
|
||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||
@ -1422,9 +1485,6 @@ Workspaces.prototype = {
|
||||
this._clearApplicationWindowSelection(false);
|
||||
}
|
||||
|
||||
let clone = this._lookupCloneForMetaWindow (metaWindow);
|
||||
clone.actor.raise_top();
|
||||
|
||||
Main.activateWindow(metaWindow, time);
|
||||
Main.overview.hide();
|
||||
},
|
||||
@ -1448,8 +1508,10 @@ Workspaces.prototype = {
|
||||
this.actor.destroy();
|
||||
this.actor = null;
|
||||
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
global.screen.disconnect(this._restackedNotifyId);
|
||||
},
|
||||
|
||||
getScale : function() {
|
||||
@ -1595,6 +1657,19 @@ Workspaces.prototype = {
|
||||
this.actor.add_actor(workspace.actor);
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_windows();
|
||||
let stackIndices = {};
|
||||
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
// Handles a drop onto the (+) button; assumes the new workspace
|
||||
// has already been added
|
||||
acceptNewWorkspaceDrop : function(source, dropActor, x, y, time) {
|
||||
|
41
po/ar.po
41
po/ar.po
@ -6,14 +6,14 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-09-25 04:22+0200\n"
|
||||
"PO-Revision-Date: 2009-09-25 04:21+0300\n"
|
||||
"POT-Creation-Date: 2009-10-03 19:19+0200\n"
|
||||
"PO-Revision-Date: 2009-10-03 19:20+0300\n"
|
||||
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
||||
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
||||
"Language: ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
"X-Generator: Virtaal 0.4.0\n"
|
||||
@ -27,12 +27,12 @@ msgid "Window management and application launching"
|
||||
msgstr "إدارة النوافذ وإطلاق التطبيقات"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:269
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "الأنشطة"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:452
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%A %Ol:%OM %p"
|
||||
|
||||
@ -41,48 +41,48 @@ msgid "Find..."
|
||||
msgstr "ابحث..."
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "Browse"
|
||||
msgstr "استعرض"
|
||||
msgid "More"
|
||||
msgstr "المزيد"
|
||||
|
||||
#: ../js/ui/dash.js:536
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(انظر الكل)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:773
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "الأماكن"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "المستندات الحديثة"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "نتائج البحث"
|
||||
|
||||
#: ../js/ui/dash.js:814
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "التفضيلات"
|
||||
|
||||
#: ../js/ui/runDialog.js:95
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "من فضلك اكتب أمرا:"
|
||||
|
||||
#: ../src/shell-global.c:799
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "منذ أقل من دقيقة"
|
||||
|
||||
#: ../src/shell-global.c:802
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
@ -93,7 +93,7 @@ msgstr[3] "منذ %d دقائق"
|
||||
msgstr[4] "منذ %d دقيقة"
|
||||
msgstr[5] "منذ %d دقيقة"
|
||||
|
||||
#: ../src/shell-global.c:805
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
@ -104,7 +104,7 @@ msgstr[3] "منذ %d ساعات"
|
||||
msgstr[4] "منذ %d ساعة"
|
||||
msgstr[5] "منذ %d ساعة"
|
||||
|
||||
#: ../src/shell-global.c:808
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
@ -115,7 +115,7 @@ msgstr[3] "منذ %d أيام"
|
||||
msgstr[4] "منذ %d يوما"
|
||||
msgstr[5] "منذ %d يوم"
|
||||
|
||||
#: ../src/shell-global.c:811
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -199,3 +199,6 @@ msgstr "ابحث"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "استعرض"
|
||||
|
52
po/ca.po
52
po/ca.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-30 18:53+0200\n"
|
||||
"PO-Revision-Date: 2009-08-30 18:57+0100\n"
|
||||
"POT-Creation-Date: 2009-10-01 23:16+0200\n"
|
||||
"PO-Revision-Date: 2009-10-01 23:20+0100\n"
|
||||
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -30,76 +30,81 @@ msgid "Activities"
|
||||
msgstr "Activitats"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:454
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/dash.js:256
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Cerca..."
|
||||
|
||||
#: ../js/ui/dash.js:374
|
||||
msgid "Browse"
|
||||
msgstr "Navega"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Més"
|
||||
|
||||
#: ../js/ui/dash.js:451
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(mostra tot)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:633
|
||||
#: ../js/ui/dash.js:681
|
||||
#: ../js/ui/dash.js:763
|
||||
#: ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICACIONS"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:653
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "LLOCS"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:660
|
||||
#: ../js/ui/dash.js:692
|
||||
#: ../js/ui/dash.js:790
|
||||
#: ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "DOCUMENTS RECENTS"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:679
|
||||
#: ../js/ui/dash.js:815
|
||||
#: ../js/ui/dash.js:958
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "RESULTATS DE LA CERCA"
|
||||
|
||||
#: ../js/ui/runDialog.js:82
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERÈNCIES"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Introduïu una ordre:"
|
||||
|
||||
#: ../src/shell-global.c:840
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Fa menys d'un minut"
|
||||
|
||||
#: ../src/shell-global.c:843
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Fa %d minut"
|
||||
msgstr[1] "Fa %d minuts"
|
||||
|
||||
#: ../src/shell-global.c:846
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Fa %d hora"
|
||||
msgstr[1] "Fa %d hores"
|
||||
|
||||
#: ../src/shell-global.c:849
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Fa %d dia"
|
||||
msgstr[1] "Fa %d dies"
|
||||
|
||||
#: ../src/shell-global.c:852
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -180,10 +185,3 @@ msgstr "Cerca"
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Cerca aplicacions o documents"
|
||||
#~ msgid "Manager"
|
||||
#~ msgstr "Gestor"
|
||||
#~ msgid "The user manager object this user is controlled by."
|
||||
#~ msgstr "L'objecte gestor d'usuaris que controla a aquest usuari."
|
||||
|
||||
|
89
po/de.po
89
po/de.po
@ -4,18 +4,20 @@
|
||||
#
|
||||
# Hendrik Brandt <heb@gnome-de.org>, 2009.
|
||||
# Hendrik Richter <hendrikr@gnome.org>, 2009.
|
||||
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-20 16:47+0200\n"
|
||||
"PO-Revision-Date: 2009-08-20 16:49+0200\n"
|
||||
"Last-Translator: Hendrik Richter <hendrikr@gnome.org>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-10-01 22:22+0000\n"
|
||||
"PO-Revision-Date: 2009-10-01 14:32+0200\n"
|
||||
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
|
||||
"Language-Team: German <gnome-de@gnome.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
@ -27,78 +29,83 @@ msgid "Window management and application launching"
|
||||
msgstr "Fenster verwalten und Anwendungen starten"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:266
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "Aktivitäten"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:433
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/dash.js:251
|
||||
msgid "Find apps or documents"
|
||||
msgstr "Anwendungen oder Dokumente suchen"
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Suchen …"
|
||||
|
||||
#: ../js/ui/dash.js:369
|
||||
msgid "Browse"
|
||||
msgstr "Durchsuchen"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Mehr"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(alle sehen)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "ANWENDUNGEN"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:598
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "ORTE"
|
||||
|
||||
#: ../js/ui/runDialog.js:75
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "SUCHERGEBNISSE"
|
||||
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "EINSTELLUNGEN"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Bitte geben Sie einen Befehl ein:"
|
||||
|
||||
#: ../src/gdmuser/gdm-user.c:243
|
||||
msgid "Manager"
|
||||
msgstr "Verwaltung"
|
||||
|
||||
#: ../src/gdmuser/gdm-user.c:244
|
||||
msgid "The user manager object this user is controlled by."
|
||||
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
||||
|
||||
#: ../src/shell-global.c:841
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Vor weniger als einer Minute"
|
||||
|
||||
#: ../src/shell-global.c:844
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Vor %d Minute"
|
||||
msgstr[1] "Vor %d Minuten"
|
||||
|
||||
#: ../src/shell-global.c:847
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Vor %d Stunde"
|
||||
msgstr[1] "Vor %d Stunden"
|
||||
|
||||
#: ../src/shell-global.c:850
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Vor %d Tag"
|
||||
msgstr[1] "Vor %d Tagen"
|
||||
|
||||
#: ../src/shell-global.c:853
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -180,3 +187,15 @@ msgstr "Suchen"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Anwendungen oder Dokumente suchen"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Durchsuchen"
|
||||
|
||||
#~ msgid "Manager"
|
||||
#~ msgstr "Verwaltung"
|
||||
|
||||
#~ msgid "The user manager object this user is controlled by."
|
||||
#~ msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
||||
|
59
po/nb.po
59
po/nb.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell 0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-21 12:36+0200\n"
|
||||
"PO-Revision-Date: 2009-08-21 12:44+0200\n"
|
||||
"POT-Creation-Date: 2009-10-03 10:47+0200\n"
|
||||
"PO-Revision-Date: 2009-10-03 10:48+0200\n"
|
||||
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
|
||||
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -25,70 +25,83 @@ msgid "Window management and application launching"
|
||||
msgstr "Vindushåndtering og oppstart av programmer"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:266
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:433
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M"
|
||||
|
||||
#: ../js/ui/dash.js:250
|
||||
msgid "Find apps or documents"
|
||||
msgstr "Finn programmer eller dokumenter"
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Finn..."
|
||||
|
||||
#: ../js/ui/dash.js:368
|
||||
msgid "Browse"
|
||||
msgstr "Bla gjennom"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Mer"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(se alle)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "PROGRAMMER"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "SISTE DOKUMENTER"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:597
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "STEDER"
|
||||
|
||||
#: ../js/ui/runDialog.js:75
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "SISTE DOKUMENTER"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "SØKERESULTATER"
|
||||
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "BRUKERVALG"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Oppgi en kommando:"
|
||||
|
||||
#: ../src/shell-global.c:841
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Mindre enn ett minutt siden"
|
||||
|
||||
#: ../src/shell-global.c:844
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "%d minutt siden"
|
||||
msgstr[1] "%d minutter siden"
|
||||
|
||||
#: ../src/shell-global.c:847
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "%d time siden"
|
||||
msgstr[1] "%d timer siden"
|
||||
|
||||
#: ../src/shell-global.c:850
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "%d dag siden"
|
||||
msgstr[1] "%d dager siden"
|
||||
|
||||
#: ../src/shell-global.c:853
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
|
148
src/Makefile-st.am
Normal file
148
src/Makefile-st.am
Normal file
@ -0,0 +1,148 @@
|
||||
st_cflags = \
|
||||
-I$(top_srcdir)/src \
|
||||
-DPREFIX=\""$(prefix)"\" \
|
||||
-DLIBDIR=\""$(libdir)"\" \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DG_LOG_DOMAIN=\"St\" \
|
||||
-DST_COMPILATION \
|
||||
-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" \
|
||||
$(ST_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
st_built_sources = \
|
||||
st-enum-types.h \
|
||||
st-enum-types.c \
|
||||
st-marshal.h \
|
||||
st-marshal.c
|
||||
|
||||
BUILT_SOURCES += $(st_built_sources)
|
||||
|
||||
EXTRA_DIST += \
|
||||
st/test-theme.css \
|
||||
st/st-marshal.list \
|
||||
st/st-enum-types.h.in \
|
||||
st/st-enum-types.c.in
|
||||
|
||||
CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h
|
||||
|
||||
st-marshal.h: stamp-st-marshal.h
|
||||
@true
|
||||
stamp-st-marshal.h: Makefile st/st-marshal.list
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_st_marshal \
|
||||
--header \
|
||||
$(srcdir)/st/st-marshal.list > $@.tmp && \
|
||||
(cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \
|
||||
rm -f $@.tmp && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
st-marshal.c: Makefile st/st-marshal.list
|
||||
$(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_st_marshal \
|
||||
--body \
|
||||
$(srcdir)/st/st-marshal.list ) > $@.tmp && \
|
||||
cp -f $@.tmp st-marshal.c && \
|
||||
rm -f $@.tmp
|
||||
|
||||
st-enum-types.h: stamp-st-enum-types.h Makefile
|
||||
@true
|
||||
stamp-st-enum-types.h: $(source_h) st/st-enum-types.h.in
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template st/st-enum-types.h.in \
|
||||
$(st_source_h) ) >> $@.tmp && \
|
||||
(cmp -s $@.tmp st-enum-types.h || cp $@.tmp st-enum-types.h) && \
|
||||
rm -f $@.tmp && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template st/st-enum-types.c.in \
|
||||
$(st_source_h) ) >> $@.tmp && \
|
||||
cp $@.tmp $@ && \
|
||||
rm -f $@.tmp
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
st_source_h = \
|
||||
st/st-adjustment.h \
|
||||
st/st-bin.h \
|
||||
st/st-border-image.h \
|
||||
st/st-box-layout.h \
|
||||
st/st-box-layout-child.h \
|
||||
st/st-button.h \
|
||||
st/st-clipboard.h \
|
||||
st/st-entry.h \
|
||||
st/st-im-text.h \
|
||||
st/st-label.h \
|
||||
st/st-private.h \
|
||||
st/st-scrollable.h \
|
||||
st/st-scroll-bar.h \
|
||||
st/st-scroll-view.h \
|
||||
st/st-subtexture.h \
|
||||
st/st-table.h \
|
||||
st/st-table-child.h \
|
||||
st/st-table-private.h \
|
||||
st/st-texture-cache.h \
|
||||
st/st-texture-frame.h \
|
||||
st/st-theme.h \
|
||||
st/st-theme-context.h \
|
||||
st/st-theme-node.h \
|
||||
st/st-theme-private.h \
|
||||
st/st-tooltip.h \
|
||||
st/st-types.h \
|
||||
st/st-widget.h \
|
||||
$(NULL)
|
||||
|
||||
st_source_private_h = \
|
||||
st/st-private.h \
|
||||
st/st-table-private.h \
|
||||
st/st-theme-private.h
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
st_source_c = \
|
||||
st/st-adjustment.c \
|
||||
st/st-bin.c \
|
||||
st/st-border-image.c \
|
||||
st/st-box-layout.c \
|
||||
st/st-box-layout-child.c \
|
||||
st/st-button.c \
|
||||
st/st-clipboard.c \
|
||||
st/st-entry.c \
|
||||
st/st-im-text.c \
|
||||
st/st-label.c \
|
||||
st/st-private.c \
|
||||
st/st-scrollable.c \
|
||||
st/st-scroll-bar.c \
|
||||
st/st-scroll-view.c \
|
||||
st/st-subtexture.c \
|
||||
st/st-table.c \
|
||||
st/st-table-child.c \
|
||||
st/st-texture-cache.c \
|
||||
st/st-texture-frame.c \
|
||||
st/st-theme.c \
|
||||
st/st-theme-context.c \
|
||||
st/st-theme-node.c \
|
||||
st/st-tooltip.c \
|
||||
st/st-widget.c \
|
||||
$(NULL)
|
||||
|
||||
noinst_LTLIBRARIES += libst-1.0.la
|
||||
|
||||
libst_1_0_la_LIBADD = $(ST_LIBS)
|
||||
libst_1_0_la_SOURCES = \
|
||||
$(st_source_c) \
|
||||
$(st_source_private_c) \
|
||||
$(st_source_h) \
|
||||
$(st_built_sources) \
|
||||
$(NULL)
|
||||
libst_1_0_la_CPPFLAGS = $(st_cflags)
|
||||
libst_1_0_la_LDFLAGS = $(LDADD)
|
||||
|
||||
noinst_PROGRAMS += test-theme
|
||||
|
||||
test_theme_CPPFLAGS = $(st_cflags)
|
||||
test_theme_LDADD = libst-1.0.la
|
||||
|
||||
test_theme_SOURCES = st/test-theme.c
|
@ -4,6 +4,7 @@ CLEANFILES =
|
||||
EXTRA_DIST =
|
||||
libexec_PROGRAMS =
|
||||
noinst_LTLIBRARIES =
|
||||
noinst_PROGRAMS =
|
||||
|
||||
.AUTOPARALLEL:
|
||||
|
||||
@ -24,6 +25,7 @@ EXTRA_DIST += gnome-shell.in
|
||||
|
||||
include Makefile-big.am
|
||||
include Makefile-gdmuser.am
|
||||
include Makefile-st.am
|
||||
include Makefile-tray.am
|
||||
|
||||
gnome_shell_cflags = \
|
||||
@ -94,7 +96,7 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-wm.c \
|
||||
shell-wm.h
|
||||
|
||||
non_gir_sources = \
|
||||
non_gir_sources = \
|
||||
shell-embedded-window-private.h
|
||||
|
||||
shell_recorder_sources = \
|
||||
@ -112,7 +114,7 @@ if BUILD_RECORDER
|
||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
|
||||
non_gir_sources += $(shell_recorder_non_gir_sources)
|
||||
|
||||
noinst_PROGRAMS = test-recorder
|
||||
noinst_PROGRAMS += test-recorder
|
||||
|
||||
test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
|
||||
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
|
||||
@ -150,23 +152,25 @@ libgnome_shell_la_LIBADD = \
|
||||
$(MUTTER_PLUGIN_LIBS) \
|
||||
$(LIBGNOMEUI_LIBS) \
|
||||
libbig-1.0.la \
|
||||
libst-1.0.la \
|
||||
libgdmuser-1.0.la \
|
||||
libtray.la
|
||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||
|
||||
typelibdir = $(pkglibdir)
|
||||
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
|
||||
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib
|
||||
|
||||
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
|
||||
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=Shell \
|
||||
--nsversion=0.1 \
|
||||
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
|
||||
--include=Clutter-1.0 \
|
||||
--include=Meta-2.27 \
|
||||
--include=Meta-2.28 \
|
||||
--libtool="$(LIBTOOL)" \
|
||||
--add-include-path=$(builddir) \
|
||||
--include=Big-1.0 \
|
||||
--include=St-1.0 \
|
||||
--program=mutter \
|
||||
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
|
||||
@ -177,14 +181,14 @@ CLEANFILES += Shell-0.1.gir
|
||||
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
|
||||
# (not the fake library, since we've already done the rewriting)
|
||||
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
|
||||
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
|
||||
$(AM_V_GEN) \
|
||||
$(G_IR_COMPILER) \
|
||||
--includedir=. \
|
||||
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
|
||||
Shell-0.1.gir -o $@
|
||||
CLEANFILES += Shell-0.1.typelib
|
||||
|
||||
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
|
||||
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=Big \
|
||||
--nsversion=1.0 \
|
||||
@ -201,6 +205,28 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
|
||||
CLEANFILES += Big-1.0.gir
|
||||
|
||||
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
|
||||
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
|
||||
$(G_IR_COMPILER) Big-1.0.gir -o $@
|
||||
$(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
|
||||
CLEANFILES += Big-1.0.typelib
|
||||
|
||||
St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=St \
|
||||
--nsversion=1.0 \
|
||||
--include=Clutter-1.0 \
|
||||
--add-include-path=$(builddir) \
|
||||
--libtool="$(LIBTOOL)" \
|
||||
--program=mutter \
|
||||
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||
-DST_COMPILATION \
|
||||
$(addprefix $(srcdir)/,$(st_source_h)) \
|
||||
$(addprefix $(srcdir)/,$(st_source_c)) \
|
||||
$(srcdir)/st-enum-types.h \
|
||||
$(st_cflags) \
|
||||
-o $@
|
||||
CLEANFILES += St-1.0.gir
|
||||
|
||||
St-1.0.typelib: St-1.0.gir
|
||||
$(AM_V_GEN) $(G_IR_COMPILER) \
|
||||
$< -o $@
|
||||
|
||||
CLEANFILES += St-1.0.typelib
|
||||
|
@ -268,6 +268,52 @@ corner_get(guint radius,
|
||||
return corner;
|
||||
}
|
||||
|
||||
/* To match the CSS specification, we want the border to look like it was
|
||||
* drawn over the background. But actually drawing the border over the
|
||||
* background will produce slightly bad antialiasing at the edges, so
|
||||
* compute the effective border color instead.
|
||||
*/
|
||||
#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
|
||||
#define MULT(c,a) NORM(c*a)
|
||||
|
||||
static void
|
||||
premultiply (ClutterColor *color)
|
||||
{
|
||||
guint t;
|
||||
color->red = MULT (color->red, color->alpha);
|
||||
color->green = MULT (color->green, color->alpha);
|
||||
color->blue = MULT (color->blue, color->alpha);
|
||||
}
|
||||
|
||||
static void
|
||||
unpremultiply (ClutterColor *color)
|
||||
{
|
||||
if (color->alpha != 0) {
|
||||
color->red = (color->red * 255 + 127) / color->alpha;
|
||||
color->green = (color->green * 255 + 127) / color->alpha;
|
||||
color->blue = (color->blue * 255 + 127) / color->alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
over (const ClutterColor *source,
|
||||
const ClutterColor *destination,
|
||||
ClutterColor *result)
|
||||
{
|
||||
guint t;
|
||||
ClutterColor src = *source;
|
||||
ClutterColor dst = *destination;
|
||||
premultiply (&src);
|
||||
premultiply (&dst);
|
||||
|
||||
result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
|
||||
result->red = src.red + NORM ((255 - src.alpha) * dst.red);
|
||||
result->green = src.green + NORM ((255 - src.alpha) * dst.green);
|
||||
result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue);
|
||||
|
||||
unpremultiply (result);
|
||||
}
|
||||
|
||||
static void
|
||||
big_rectangle_update_corners(BigRectangle *rectangle)
|
||||
{
|
||||
@ -278,6 +324,7 @@ big_rectangle_update_corners(BigRectangle *rectangle)
|
||||
if (rectangle->radius != 0) {
|
||||
ClutterColor *color;
|
||||
ClutterColor *border_color;
|
||||
ClutterColor effective_border;
|
||||
guint border_width;
|
||||
|
||||
g_object_get(rectangle,
|
||||
@ -286,10 +333,12 @@ big_rectangle_update_corners(BigRectangle *rectangle)
|
||||
"color", &color,
|
||||
NULL);
|
||||
|
||||
over (border_color, color, &effective_border);
|
||||
|
||||
corner = corner_get(rectangle->radius,
|
||||
color,
|
||||
border_width,
|
||||
border_color);
|
||||
&effective_border);
|
||||
|
||||
clutter_color_free(border_color);
|
||||
clutter_color_free(color);
|
||||
@ -329,12 +378,10 @@ big_rectangle_paint(ClutterActor *actor)
|
||||
|
||||
rectangle = BIG_RECTANGLE(actor);
|
||||
|
||||
if (rectangle->radius == 0) {
|
||||
/* In that case we are no different than our parent class,
|
||||
* so don't bother */
|
||||
CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
|
||||
return;
|
||||
}
|
||||
/* We can't chain up, even when we the radius is 0, because of the different
|
||||
* interpretation of the border/background relationship here than for
|
||||
* ClutterRectangle.
|
||||
*/
|
||||
|
||||
if (rectangle->corners_dirty)
|
||||
big_rectangle_update_corners(rectangle);
|
||||
@ -345,6 +392,9 @@ big_rectangle_paint(ClutterActor *actor)
|
||||
"color", &color,
|
||||
NULL);
|
||||
|
||||
if (border_color->alpha == 0 && color->alpha == 0)
|
||||
goto out;
|
||||
|
||||
actor_opacity = clutter_actor_get_paint_opacity (actor);
|
||||
|
||||
clutter_actor_get_allocation_box(actor, &box);
|
||||
@ -358,6 +408,11 @@ big_rectangle_paint(ClutterActor *actor)
|
||||
|
||||
radius = rectangle->radius;
|
||||
|
||||
/* Optimization; if the border is transparent, it just looks like part of
|
||||
* the background */
|
||||
if (radius == 0 && border_color->alpha == 0)
|
||||
border_width = 0;
|
||||
|
||||
max = MAX(border_width, radius);
|
||||
|
||||
if (radius != 0) {
|
||||
@ -393,33 +448,54 @@ big_rectangle_paint(ClutterActor *actor)
|
||||
}
|
||||
|
||||
if (border_width != 0) {
|
||||
ClutterColor effective_border;
|
||||
over (border_color, color, &effective_border);
|
||||
|
||||
if (!rectangle->border_material)
|
||||
rectangle->border_material = cogl_material_new ();
|
||||
|
||||
cogl_color_set_from_4ub(&tmp_color,
|
||||
border_color->red,
|
||||
border_color->green,
|
||||
border_color->blue,
|
||||
actor_opacity * border_color->alpha / 255);
|
||||
effective_border.red,
|
||||
effective_border.green,
|
||||
effective_border.blue,
|
||||
actor_opacity * effective_border.alpha / 255);
|
||||
cogl_color_premultiply (&tmp_color);
|
||||
cogl_material_set_color(rectangle->border_material, &tmp_color);
|
||||
cogl_set_source(rectangle->border_material);
|
||||
|
||||
/* NORTH */
|
||||
cogl_rectangle(max, 0,
|
||||
width - max, border_width);
|
||||
if (radius > 0) { /* skip corners */
|
||||
/* NORTH */
|
||||
cogl_rectangle(max, 0,
|
||||
width - max, border_width);
|
||||
|
||||
/* EAST */
|
||||
cogl_rectangle(width - border_width, max,
|
||||
width, height - max);
|
||||
/* EAST */
|
||||
cogl_rectangle(width - border_width, max,
|
||||
width, height - max);
|
||||
|
||||
/* SOUTH */
|
||||
cogl_rectangle(max, height - border_width,
|
||||
width - max, height);
|
||||
/* SOUTH */
|
||||
cogl_rectangle(max, height - border_width,
|
||||
width - max, height);
|
||||
|
||||
/* WEST */
|
||||
cogl_rectangle(0, max,
|
||||
border_width, height - max);
|
||||
/* WEST */
|
||||
cogl_rectangle(0, max,
|
||||
border_width, height - max);
|
||||
} else { /* include corners */
|
||||
/* NORTH */
|
||||
cogl_rectangle(0, 0,
|
||||
width, border_width);
|
||||
|
||||
/* EAST */
|
||||
cogl_rectangle(width - border_width, border_width,
|
||||
width, height - border_width);
|
||||
|
||||
/* SOUTH */
|
||||
cogl_rectangle(0, height - border_width,
|
||||
width, height);
|
||||
|
||||
/* WEST */
|
||||
cogl_rectangle(0, border_width,
|
||||
border_width, height - border_width);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rectangle->background_material)
|
||||
@ -455,6 +531,7 @@ big_rectangle_paint(ClutterActor *actor)
|
||||
cogl_rectangle(border_width, max,
|
||||
width - border_width, height - max);
|
||||
|
||||
out:
|
||||
clutter_color_free(border_color);
|
||||
clutter_color_free(color);
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <mutter-plugin.h>
|
||||
|
||||
ClutterActor *
|
||||
mutter_plugin_get_overlay_group (MutterPlugin *plugin)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
mutter_plugin_get_stage (MutterPlugin *plugin)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GList *
|
||||
mutter_plugin_get_windows (MutterPlugin *plugin)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mutter_plugin_query_screen_size (MutterPlugin *plugin,
|
||||
int *width,
|
||||
int *height)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
|
||||
gint x, gint y, gint width, gint height)
|
||||
{
|
||||
}
|
||||
|
||||
MetaScreen *
|
||||
mutter_plugin_get_screen (MutterPlugin *plugin)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
mutter_plugin_get_window_group (MutterPlugin *plugin)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Display *
|
||||
meta_display_get_xdisplay (MetaDisplay *display)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MetaDisplay *
|
||||
meta_screen_get_display (MetaScreen *display)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Window
|
||||
meta_screen_get_xroot (MetaScreen *display)
|
||||
{
|
||||
return None;
|
||||
}
|
@ -45,6 +45,7 @@ struct _ShellAppSystemPrivate {
|
||||
|
||||
GHashTable *app_id_to_app;
|
||||
|
||||
GHashTable *cached_menu_contents; /* <char *id, GSList<ShellAppInfo*>> */
|
||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
||||
|
||||
GSList *cached_settings; /* ShellAppInfo */
|
||||
@ -56,6 +57,7 @@ struct _ShellAppSystemPrivate {
|
||||
guint app_change_timeout_id;
|
||||
};
|
||||
|
||||
static void free_appinfo_gslist (gpointer list);
|
||||
static void shell_app_system_finalize (GObject *object);
|
||||
static gboolean on_tree_changed (gpointer user_data);
|
||||
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
|
||||
@ -229,6 +231,9 @@ shell_app_system_init (ShellAppSystem *self)
|
||||
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||
|
||||
priv->cached_menu_contents = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, free_appinfo_gslist);
|
||||
|
||||
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
||||
* handle NODISPLAY semantics at a higher level or investigate them
|
||||
* case by case.
|
||||
@ -262,6 +267,8 @@ shell_app_system_finalize (GObject *object)
|
||||
gmenu_tree_unref (priv->apps_tree);
|
||||
gmenu_tree_unref (priv->settings_tree);
|
||||
|
||||
g_hash_table_destroy (priv->cached_menu_contents);
|
||||
|
||||
g_hash_table_destroy (priv->app_id_to_app);
|
||||
|
||||
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
||||
@ -279,6 +286,14 @@ shell_app_system_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void
|
||||
free_appinfo_gslist (gpointer listp)
|
||||
{
|
||||
GSList *list = listp;
|
||||
g_slist_foreach (list, (GFunc) shell_app_info_unref, NULL);
|
||||
g_slist_free (list);
|
||||
}
|
||||
|
||||
static void
|
||||
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
||||
{
|
||||
@ -419,8 +434,12 @@ static gboolean
|
||||
on_tree_changed (gpointer user_data)
|
||||
{
|
||||
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
reread_menus (self);
|
||||
g_hash_table_remove_all (self->priv->cached_menu_contents);
|
||||
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
self->priv->app_change_timeout_id = 0;
|
||||
return FALSE;
|
||||
}
|
||||
@ -537,26 +556,32 @@ shell_app_menu_entry_get_type (void)
|
||||
* shell_app_system_get_applications_for_menu:
|
||||
*
|
||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||
* are flattened.
|
||||
* are flattened. This value is computed on initial call and cached thereafter
|
||||
* until the set of installed applications changes.
|
||||
*
|
||||
* Return value: (transfer full) (element-type ShellAppInfo): List of applications
|
||||
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *self,
|
||||
const char *menu)
|
||||
{
|
||||
char *path;
|
||||
GMenuTreeDirectory *menu_entry;
|
||||
GSList *apps;
|
||||
|
||||
path = g_strdup_printf ("/%s", menu);
|
||||
menu_entry = gmenu_tree_get_directory_from_path (monitor->priv->apps_tree, path);
|
||||
g_free (path);
|
||||
g_assert (menu_entry != NULL);
|
||||
apps = g_hash_table_lookup (self->priv->cached_menu_contents, menu);
|
||||
if (!apps)
|
||||
{
|
||||
char *path;
|
||||
GMenuTreeDirectory *menu_entry;
|
||||
path = g_strdup_printf ("/%s", menu);
|
||||
menu_entry = gmenu_tree_get_directory_from_path (self->priv->apps_tree, path);
|
||||
g_free (path);
|
||||
g_assert (menu_entry != NULL);
|
||||
|
||||
apps = gather_entries_recurse (monitor, NULL, menu_entry);
|
||||
apps = gather_entries_recurse (self, NULL, menu_entry);
|
||||
g_hash_table_insert (self->priv->cached_menu_contents, g_strdup (menu), apps);
|
||||
|
||||
gmenu_tree_item_unref (menu_entry);
|
||||
gmenu_tree_item_unref (menu_entry);
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
@ -147,17 +147,14 @@ shell_draw_clock (ClutterCairoTexture *texture,
|
||||
}
|
||||
|
||||
void
|
||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ClutterGravity pointing_towards,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color)
|
||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ShellPointerDirection direction,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color)
|
||||
{
|
||||
guint width, height;
|
||||
cairo_t *cr;
|
||||
|
||||
g_return_if_fail (pointing_towards == CLUTTER_GRAVITY_NORTH ||
|
||||
pointing_towards == CLUTTER_GRAVITY_WEST);
|
||||
|
||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||
|
||||
clutter_cairo_texture_clear (texture);
|
||||
@ -167,17 +164,34 @@ shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
|
||||
clutter_cairo_set_source_color (cr, border_color);
|
||||
|
||||
if (pointing_towards == CLUTTER_GRAVITY_WEST)
|
||||
{
|
||||
cairo_move_to (cr, width, 0);
|
||||
cairo_line_to (cr, 0, floor (height * 0.5));
|
||||
cairo_line_to (cr, width, height);
|
||||
}
|
||||
else /* CLUTTER_GRAVITY_NORTH */
|
||||
switch (direction)
|
||||
{
|
||||
case SHELL_POINTER_UP:
|
||||
cairo_move_to (cr, 0, height);
|
||||
cairo_line_to (cr, floor (width * 0.5), 0);
|
||||
cairo_line_to (cr, width, height);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_DOWN:
|
||||
cairo_move_to (cr, width, 0);
|
||||
cairo_line_to (cr, floor (width * 0.5), height);
|
||||
cairo_line_to (cr, 0, 0);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_LEFT:
|
||||
cairo_move_to (cr, width, height);
|
||||
cairo_line_to (cr, 0, floor (height * 0.5));
|
||||
cairo_line_to (cr, width, 0);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_RIGHT:
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_line_to (cr, width, floor (height * 0.5));
|
||||
cairo_line_to (cr, 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
cairo_stroke_preserve (cr);
|
||||
|
@ -13,10 +13,17 @@ ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
|
||||
ClutterColor *right);
|
||||
|
||||
void shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ClutterGravity pointing_towards,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color);
|
||||
typedef enum {
|
||||
SHELL_POINTER_UP,
|
||||
SHELL_POINTER_DOWN,
|
||||
SHELL_POINTER_LEFT,
|
||||
SHELL_POINTER_RIGHT
|
||||
} ShellPointerDirection;
|
||||
|
||||
void shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ShellPointerDirection direction,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color);
|
||||
|
||||
void shell_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
|
@ -40,6 +40,7 @@ struct _ShellGlobal {
|
||||
|
||||
MutterPlugin *plugin;
|
||||
ShellWM *wm;
|
||||
const char *datadir;
|
||||
const char *imagedir;
|
||||
const char *configdir;
|
||||
|
||||
@ -57,6 +58,7 @@ enum {
|
||||
PROP_STAGE,
|
||||
PROP_WINDOW_GROUP,
|
||||
PROP_WINDOW_MANAGER,
|
||||
PROP_DATADIR,
|
||||
PROP_IMAGEDIR,
|
||||
PROP_CONFIGDIR,
|
||||
};
|
||||
@ -128,6 +130,9 @@ shell_global_get_property(GObject *object,
|
||||
case PROP_WINDOW_MANAGER:
|
||||
g_value_set_object (value, global->wm);
|
||||
break;
|
||||
case PROP_DATADIR:
|
||||
g_value_set_string (value, global->datadir);
|
||||
break;
|
||||
case PROP_IMAGEDIR:
|
||||
g_value_set_string (value, global->imagedir);
|
||||
break;
|
||||
@ -149,6 +154,7 @@ shell_global_init (ShellGlobal *global)
|
||||
|
||||
if (!datadir)
|
||||
datadir = GNOME_SHELL_DATADIR;
|
||||
global->datadir = datadir;
|
||||
|
||||
/* We make sure imagedir ends with a '/', since the JS won't have
|
||||
* access to g_build_filename() and so will end up just
|
||||
@ -254,6 +260,13 @@ shell_global_class_init (ShellGlobalClass *klass)
|
||||
"Window management interface",
|
||||
SHELL_TYPE_WM,
|
||||
G_PARAM_READABLE));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DATADIR,
|
||||
g_param_spec_string ("datadir",
|
||||
"Data directory",
|
||||
"Directory containing gnome-shell data files",
|
||||
NULL,
|
||||
G_PARAM_READABLE));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_IMAGEDIR,
|
||||
g_param_spec_string ("imagedir",
|
||||
@ -869,3 +882,138 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
|
||||
|
||||
return clutter_clone_new (global->root_pixmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_monitors:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets a list of the bounding boxes of the active screen's monitors.
|
||||
*
|
||||
* Return value: (transfer full) (element-type GdkRectangle): a list
|
||||
* of monitor bounding boxes.
|
||||
*/
|
||||
GSList *
|
||||
shell_global_get_monitors (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
GSList *monitors = NULL;
|
||||
MetaRectangle rect;
|
||||
int i;
|
||||
|
||||
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
|
||||
|
||||
for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--)
|
||||
{
|
||||
meta_screen_get_monitor_geometry (screen, i, &rect);
|
||||
monitors = g_slist_prepend (monitors,
|
||||
g_boxed_copy (GDK_TYPE_RECTANGLE, &rect));
|
||||
}
|
||||
return monitors;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_primary_monitor:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the bounding box of the primary monitor (the one that the
|
||||
* panel is on).
|
||||
*
|
||||
* Return value: the bounding box of the primary monitor
|
||||
*/
|
||||
GdkRectangle *
|
||||
shell_global_get_primary_monitor (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
MetaRectangle rect;
|
||||
|
||||
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
|
||||
|
||||
meta_screen_get_monitor_geometry (screen, 0, &rect);
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_focus_monitor:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the bounding box of the monitor containing the window that
|
||||
* currently contains the keyboard focus.
|
||||
*
|
||||
* Return value: the bounding box of the focus monitor
|
||||
*/
|
||||
GdkRectangle *
|
||||
shell_global_get_focus_monitor (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
MetaWindow *focus = meta_display_get_focus_window (display);
|
||||
MetaRectangle rect, wrect;
|
||||
int nmonitors, i;
|
||||
|
||||
if (focus)
|
||||
{
|
||||
meta_window_get_outer_rect (focus, &wrect);
|
||||
nmonitors = meta_screen_get_n_monitors (screen);
|
||||
|
||||
/* Find the monitor that the top-left corner of @focus is on. */
|
||||
for (i = 0; i < nmonitors; i++)
|
||||
{
|
||||
meta_screen_get_monitor_geometry (screen, i, &rect);
|
||||
|
||||
if (rect.x < wrect.x && rect.y < wrect.y &&
|
||||
rect.x + rect.width > wrect.x &&
|
||||
rect.y + rect.height > wrect.y)
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
meta_screen_get_monitor_geometry (screen, 0, &rect);
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_modifier_keys:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the current set of modifier keys that are pressed down;
|
||||
* this is a wrapper around gdk_display_get_pointer() that strips
|
||||
* out any un-declared modifier flags, to make gjs happy; see
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
|
||||
*
|
||||
* Return value: the current modifiers
|
||||
*/
|
||||
GdkModifierType
|
||||
shell_global_get_modifier_keys (ShellGlobal *global)
|
||||
{
|
||||
GdkModifierType mods;
|
||||
|
||||
gdk_display_get_pointer (gdk_display_get_default (), NULL, NULL, NULL, &mods);
|
||||
return mods & GDK_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_get_event_state:
|
||||
* @event: a #ClutterEvent
|
||||
*
|
||||
* Gets the current state of the event (the set of modifier keys that
|
||||
* are pressed down). Thhis is a wrapper around
|
||||
* clutter_event_get_state() that strips out any un-declared modifier
|
||||
* flags, to make gjs happy; see
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
|
||||
*
|
||||
* Return value: the state from the event
|
||||
*/
|
||||
ClutterModifierType
|
||||
shell_get_event_state (ClutterEvent *event)
|
||||
{
|
||||
ClutterModifierType state = clutter_event_get_state (event);
|
||||
return state & CLUTTER_MODIFIER_MASK;
|
||||
}
|
||||
|
@ -73,6 +73,14 @@ void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta,
|
||||
|
||||
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
|
||||
|
||||
GSList *shell_global_get_monitors (ShellGlobal *global);
|
||||
GdkRectangle *shell_global_get_primary_monitor (ShellGlobal *global);
|
||||
GdkRectangle *shell_global_get_focus_monitor (ShellGlobal *global);
|
||||
|
||||
GdkModifierType shell_global_get_modifier_keys (ShellGlobal *global);
|
||||
|
||||
ClutterModifierType shell_get_event_state (ClutterEvent *event);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_GLOBAL_H__ */
|
||||
|
@ -15,6 +15,7 @@ G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX);
|
||||
struct _ShellMenuPrivate {
|
||||
gboolean popped_up;
|
||||
gboolean have_grab;
|
||||
guint activating_button;
|
||||
|
||||
gboolean released_on_source;
|
||||
ClutterActor *source_actor;
|
||||
@ -117,9 +118,15 @@ shell_menu_button_release_event (ClutterActor *actor,
|
||||
{
|
||||
ShellMenu *box = SHELL_MENU (actor);
|
||||
|
||||
if (event->button != 1)
|
||||
/* Until the user releases the button that brought up the menu, we just
|
||||
* ignore other button press/releass.
|
||||
* See https://bugzilla.gnome.org/show_bug.cgi?id=596371
|
||||
*/
|
||||
if (box->priv->activating_button > 0 && box->priv->activating_button != event->button)
|
||||
return FALSE;
|
||||
|
||||
box->priv->activating_button = 0;
|
||||
|
||||
if (box->priv->source_actor && !box->priv->released_on_source)
|
||||
{
|
||||
if (box->priv->source_actor == event->source ||
|
||||
@ -134,13 +141,8 @@ shell_menu_button_release_event (ClutterActor *actor,
|
||||
|
||||
shell_menu_popdown_nosignal (box);
|
||||
|
||||
if (!container_contains (CLUTTER_CONTAINER (box), event->source))
|
||||
{
|
||||
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (box->priv->selected == NULL)
|
||||
if (!container_contains (CLUTTER_CONTAINER (box), event->source) ||
|
||||
box->priv->selected == NULL)
|
||||
{
|
||||
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
|
||||
return FALSE;
|
||||
@ -158,6 +160,7 @@ shell_menu_popup (ShellMenu *box,
|
||||
{
|
||||
if (box->priv->popped_up)
|
||||
return;
|
||||
box->priv->activating_button = button;
|
||||
box->priv->popped_up = TRUE;
|
||||
box->priv->have_grab = TRUE;
|
||||
box->priv->released_on_source = FALSE;
|
||||
|
@ -1,240 +0,0 @@
|
||||
#include "shell-panel-window.h"
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define PANEL_HEIGHT 25
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
};
|
||||
|
||||
static void shell_panel_window_finalize (GObject *object);
|
||||
static void shell_panel_window_size_request (GtkWidget *self, GtkRequisition *req);
|
||||
static void shell_panel_window_size_allocate (GtkWidget *self, GtkAllocation *allocation);
|
||||
static void shell_panel_window_realize (GtkWidget *self);
|
||||
static void shell_panel_window_show (GtkWidget *self);
|
||||
static void set_strut (ShellPanelWindow *self);
|
||||
static void on_workarea_changed (ShellPanelWindow *self);
|
||||
static void handle_new_workarea (ShellPanelWindow *self);
|
||||
static GdkFilterReturn filter_func (GdkXEvent *xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data);
|
||||
|
||||
G_DEFINE_TYPE(ShellPanelWindow, shell_panel_window, GTK_TYPE_WINDOW);
|
||||
|
||||
struct ShellPanelWindowPrivate {
|
||||
GtkAllocation workarea;
|
||||
guint width;
|
||||
guint height;
|
||||
Atom workarea_atom;
|
||||
};
|
||||
|
||||
static void
|
||||
shell_panel_window_class_init(ShellPanelWindowClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *)klass;
|
||||
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
|
||||
|
||||
gobject_class->finalize = shell_panel_window_finalize;
|
||||
|
||||
widget_class->realize = shell_panel_window_realize;
|
||||
widget_class->size_request = shell_panel_window_size_request;
|
||||
widget_class->size_allocate = shell_panel_window_size_allocate;
|
||||
widget_class->show = shell_panel_window_show;
|
||||
}
|
||||
|
||||
static void shell_panel_window_init (ShellPanelWindow *self)
|
||||
{
|
||||
self->priv = g_new0 (ShellPanelWindowPrivate, 1);
|
||||
|
||||
self->priv->workarea_atom = gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (), "_NET_WORKAREA");
|
||||
|
||||
gtk_window_set_type_hint (GTK_WINDOW (self), GDK_WINDOW_TYPE_HINT_DOCK);
|
||||
gtk_window_set_focus_on_map (GTK_WINDOW (self), FALSE);
|
||||
gdk_window_add_filter (NULL, filter_func, self);
|
||||
}
|
||||
|
||||
static void shell_panel_window_finalize (GObject *object)
|
||||
{
|
||||
ShellPanelWindow *self = (ShellPanelWindow*)object;
|
||||
|
||||
g_free (self->priv);
|
||||
g_signal_handlers_destroy(object);
|
||||
G_OBJECT_CLASS (shell_panel_window_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
ShellPanelWindow* shell_panel_window_new(void) {
|
||||
return (ShellPanelWindow*) g_object_new(SHELL_TYPE_PANEL_WINDOW,
|
||||
"type", GTK_WINDOW_TOPLEVEL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
set_strut (ShellPanelWindow *self)
|
||||
{
|
||||
long *buf;
|
||||
int strut_size;
|
||||
|
||||
strut_size = GTK_WIDGET (self)->allocation.height;
|
||||
|
||||
buf = g_new0 (long, 4);
|
||||
buf[0] = 0; /* left */
|
||||
buf[1] = 0; /* right */
|
||||
buf[2] = 0; /* top */
|
||||
buf[3] = strut_size; /* bottom */
|
||||
gdk_property_change (GTK_WIDGET (self)->window, gdk_atom_intern_static_string ("_NET_WM_STRUT"),
|
||||
gdk_atom_intern_static_string ("CARDINAL"), 32,
|
||||
GDK_PROP_MODE_REPLACE,
|
||||
(guchar*) buf, 4);
|
||||
g_free (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_panel_window_size_request (GtkWidget *widget, GtkRequisition *requisition)
|
||||
{
|
||||
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
|
||||
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->size_request(widget, requisition);
|
||||
requisition->width = self->priv->width;
|
||||
requisition->height = PANEL_HEIGHT;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_panel_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
|
||||
{
|
||||
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
|
||||
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->size_allocate(widget, allocation);
|
||||
if (GTK_WIDGET_REALIZED (self))
|
||||
set_strut (self);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_panel_window_realize (GtkWidget *widget)
|
||||
{
|
||||
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
|
||||
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->realize(widget);
|
||||
set_strut (self);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_panel_window_show (GtkWidget *widget)
|
||||
{
|
||||
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
|
||||
on_workarea_changed (self);
|
||||
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->show(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_new_workarea (ShellPanelWindow *self)
|
||||
{
|
||||
GtkRequisition requisition;
|
||||
int x, y;
|
||||
int width;
|
||||
int height;
|
||||
int x_target, y_target;
|
||||
|
||||
gtk_widget_size_request (GTK_WIDGET (self), &requisition);
|
||||
|
||||
/* If we don't have a workarea, just use monitor */
|
||||
if (self->priv->workarea.width == 0)
|
||||
{
|
||||
int monitor;
|
||||
GdkRectangle monitor_geometry;
|
||||
|
||||
monitor = gdk_screen_get_monitor_at_point (gdk_screen_get_default (),
|
||||
0, 0);
|
||||
gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
|
||||
monitor, &monitor_geometry);
|
||||
x = monitor_geometry.x;
|
||||
y = monitor_geometry.y;
|
||||
width = monitor_geometry.width;
|
||||
height = monitor_geometry.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = self->priv->workarea.x;
|
||||
y = self->priv->workarea.y;
|
||||
width = self->priv->workarea.width;
|
||||
height = self->priv->workarea.height;
|
||||
}
|
||||
|
||||
x_target = x;
|
||||
y_target = y + height - requisition.height;
|
||||
|
||||
self->priv->width = width;
|
||||
self->priv->height = height;
|
||||
gtk_widget_set_size_request (GTK_WIDGET (self), width - x_target, PANEL_HEIGHT);
|
||||
gtk_window_move (GTK_WINDOW (self), x_target, y_target);
|
||||
}
|
||||
|
||||
static void
|
||||
on_workarea_changed (ShellPanelWindow *self)
|
||||
{
|
||||
gulong bytes_after, nitems;
|
||||
Atom type;
|
||||
gint format;
|
||||
guchar *data;
|
||||
long *data32;
|
||||
Atom workarea = gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (), "_NET_WORKAREA");
|
||||
|
||||
XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||
workarea,
|
||||
0, 4, FALSE, workarea,
|
||||
&type, &format, &nitems, &bytes_after, &data);
|
||||
if ((format == 32) && (nitems == 4) && (bytes_after == 0))
|
||||
{
|
||||
int x, y, width, height;
|
||||
data32 = (long*) data;
|
||||
x = data32[0];
|
||||
y = data32[1];
|
||||
width = data32[2];
|
||||
height = data32[3];
|
||||
if (x == self->priv->workarea.x && y == self->priv->workarea.y
|
||||
&& width == self->priv->workarea.width
|
||||
&& height == self->priv->workarea.height)
|
||||
return;
|
||||
|
||||
self->priv->workarea.x = x;
|
||||
self->priv->workarea.y = y;
|
||||
self->priv->workarea.width = width;
|
||||
self->priv->workarea.height = height;
|
||||
|
||||
handle_new_workarea (self);
|
||||
}
|
||||
else if (nitems == 0)
|
||||
{
|
||||
/* We have no workarea set; assume there are no other panels at this time */
|
||||
self->priv->workarea.x = self->priv->workarea.y = 0;
|
||||
self->priv->workarea.width = self->priv->workarea.height = 0;
|
||||
handle_new_workarea (self);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("unexpected return from XGetWindowProperty: %d %ld %ld\n",
|
||||
format, nitems, bytes_after);
|
||||
}
|
||||
}
|
||||
|
||||
static GdkFilterReturn
|
||||
filter_func (GdkXEvent *gdk_xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data)
|
||||
{
|
||||
ShellPanelWindow *self = SHELL_PANEL_WINDOW (data);
|
||||
GdkFilterReturn ret = GDK_FILTER_CONTINUE;
|
||||
XEvent *xevent = (XEvent *) event;
|
||||
|
||||
switch (xevent->type)
|
||||
{
|
||||
case PropertyNotify:
|
||||
{
|
||||
if (xevent->xproperty.atom != self->priv->workarea_atom)
|
||||
break;
|
||||
on_workarea_changed (self);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#ifndef __SHELL_PANEL_WINDOW_H__
|
||||
#define __SHELL_PANEL_WINDOW_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_PANEL_WINDOW (shell_panel_window_get_type ())
|
||||
#define SHELL_PANEL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindow))
|
||||
#define SHELL_PANEL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindowClass))
|
||||
#define SHELL_IS_PANEL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_PANEL_WINDOW))
|
||||
#define SHELL_IS_PANEL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_PANEL_WINDOW))
|
||||
#define SHELL_PANEL_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindowClass))
|
||||
|
||||
typedef struct _ShellPanelWindow ShellPanelWindow;
|
||||
typedef struct _ShellPanelWindowClass ShellPanelWindowClass;
|
||||
|
||||
typedef struct ShellPanelWindowPrivate ShellPanelWindowPrivate;
|
||||
|
||||
struct _ShellPanelWindow
|
||||
{
|
||||
GtkWindow parent;
|
||||
|
||||
ShellPanelWindowPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellPanelWindowClass
|
||||
{
|
||||
GtkWindowClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
GType shell_panel_window_get_type (void) G_GNUC_CONST;
|
||||
ShellPanelWindow* shell_panel_window_new(void);
|
||||
|
||||
|
||||
#endif /* __SHELL_PANEL_WINDOW_H__ */
|
777
src/st/st-adjustment.c
Normal file
777
src/st/st-adjustment.c
Normal file
@ -0,0 +1,777 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-adjustment.c: Adjustment object
|
||||
*
|
||||
* Copyright (C) 2008 OpenedHand
|
||||
* Copyright (c) 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-adjustment
|
||||
* @short_description: A GObject representing an adjustable bounded value
|
||||
*
|
||||
* The #StAdjustment object represents a range of values bounded between a
|
||||
* minimum and maximum, together with step and page increments and a page size.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-adjustment.h"
|
||||
#include "st-marshal.h"
|
||||
#include "st-private.h"
|
||||
|
||||
G_DEFINE_TYPE (StAdjustment, st_adjustment, G_TYPE_OBJECT)
|
||||
|
||||
#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_ADJUSTMENT, StAdjustmentPrivate))
|
||||
|
||||
struct _StAdjustmentPrivate
|
||||
{
|
||||
/* Do not sanity-check values while constructing,
|
||||
* not all properties may be set yet. */
|
||||
gboolean is_constructing : 1;
|
||||
|
||||
gdouble lower;
|
||||
gdouble upper;
|
||||
gdouble value;
|
||||
gdouble step_increment;
|
||||
gdouble page_increment;
|
||||
gdouble page_size;
|
||||
|
||||
/* For interpolation */
|
||||
ClutterTimeline *interpolation;
|
||||
gdouble old_position;
|
||||
gdouble new_position;
|
||||
|
||||
/* For elasticity */
|
||||
gboolean elastic;
|
||||
guint bounce_source;
|
||||
ClutterAlpha *bounce_alpha;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_LOWER,
|
||||
PROP_UPPER,
|
||||
PROP_VALUE,
|
||||
PROP_STEP_INC,
|
||||
PROP_PAGE_INC,
|
||||
PROP_PAGE_SIZE,
|
||||
|
||||
PROP_ELASTIC,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CHANGED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
|
||||
gdouble lower);
|
||||
static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
|
||||
gdouble upper);
|
||||
static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
|
||||
gdouble step);
|
||||
static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
|
||||
gdouble page);
|
||||
static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
|
||||
gdouble size);
|
||||
|
||||
static void
|
||||
st_adjustment_constructed (GObject *object)
|
||||
{
|
||||
GObjectClass *g_class;
|
||||
StAdjustment *self = ST_ADJUSTMENT (object);
|
||||
|
||||
g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
|
||||
/* The docs say we're suppose to chain up, but would crash without
|
||||
* some extra care. */
|
||||
if (g_class && g_class->constructed &&
|
||||
g_class->constructed != st_adjustment_constructed)
|
||||
{
|
||||
g_class->constructed (object);
|
||||
}
|
||||
|
||||
ST_ADJUSTMENT (self)->priv->is_constructing = FALSE;
|
||||
st_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
|
||||
}
|
||||
|
||||
static void
|
||||
st_adjustment_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StAdjustmentPrivate *priv = ST_ADJUSTMENT (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LOWER:
|
||||
g_value_set_double (value, priv->lower);
|
||||
break;
|
||||
|
||||
case PROP_UPPER:
|
||||
g_value_set_double (value, priv->upper);
|
||||
break;
|
||||
|
||||
case PROP_VALUE:
|
||||
g_value_set_double (value, priv->value);
|
||||
break;
|
||||
|
||||
case PROP_STEP_INC:
|
||||
g_value_set_double (value, priv->step_increment);
|
||||
break;
|
||||
|
||||
case PROP_PAGE_INC:
|
||||
g_value_set_double (value, priv->page_increment);
|
||||
break;
|
||||
|
||||
case PROP_PAGE_SIZE:
|
||||
g_value_set_double (value, priv->page_size);
|
||||
break;
|
||||
|
||||
case PROP_ELASTIC:
|
||||
g_value_set_boolean (value, priv->elastic);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_adjustment_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StAdjustment *adj = ST_ADJUSTMENT (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LOWER:
|
||||
st_adjustment_set_lower (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_UPPER:
|
||||
st_adjustment_set_upper (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_VALUE:
|
||||
st_adjustment_set_value (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_STEP_INC:
|
||||
st_adjustment_set_step_increment (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_PAGE_INC:
|
||||
st_adjustment_set_page_increment (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_PAGE_SIZE:
|
||||
st_adjustment_set_page_size (adj, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_ELASTIC:
|
||||
st_adjustment_set_elastic (adj, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_interpolation (StAdjustment *adjustment)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->interpolation)
|
||||
{
|
||||
clutter_timeline_stop (priv->interpolation);
|
||||
g_object_unref (priv->interpolation);
|
||||
priv->interpolation = NULL;
|
||||
|
||||
if (priv->bounce_alpha)
|
||||
{
|
||||
g_object_unref (priv->bounce_alpha);
|
||||
priv->bounce_alpha = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->bounce_source)
|
||||
{
|
||||
g_source_remove (priv->bounce_source);
|
||||
priv->bounce_source = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_adjustment_dispose (GObject *object)
|
||||
{
|
||||
stop_interpolation (ST_ADJUSTMENT (object));
|
||||
|
||||
G_OBJECT_CLASS (st_adjustment_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_adjustment_class_init (StAdjustmentClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StAdjustmentPrivate));
|
||||
|
||||
object_class->constructed = st_adjustment_constructed;
|
||||
object_class->get_property = st_adjustment_get_property;
|
||||
object_class->set_property = st_adjustment_set_property;
|
||||
object_class->dispose = st_adjustment_dispose;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_LOWER,
|
||||
g_param_spec_double ("lower",
|
||||
"Lower",
|
||||
"Lower bound",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_UPPER,
|
||||
g_param_spec_double ("upper",
|
||||
"Upper",
|
||||
"Upper bound",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_VALUE,
|
||||
g_param_spec_double ("value",
|
||||
"Value",
|
||||
"Current value",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_STEP_INC,
|
||||
g_param_spec_double ("step-increment",
|
||||
"Step Increment",
|
||||
"Step increment",
|
||||
0.0,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PAGE_INC,
|
||||
g_param_spec_double ("page-increment",
|
||||
"Page Increment",
|
||||
"Page increment",
|
||||
0.0,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PAGE_SIZE,
|
||||
g_param_spec_double ("page-size",
|
||||
"Page Size",
|
||||
"Page size",
|
||||
0.0,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_ELASTIC,
|
||||
g_param_spec_boolean ("elastic",
|
||||
"Elastic",
|
||||
"Make interpolation "
|
||||
"behave in an "
|
||||
"'elastic' way and "
|
||||
"stop clamping value.",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
|
||||
/**
|
||||
* StAdjustment::changed:
|
||||
*
|
||||
* Emitted when any of the adjustment values have changed
|
||||
*/
|
||||
signals[CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StAdjustmentClass, changed),
|
||||
NULL, NULL,
|
||||
_st_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
st_adjustment_init (StAdjustment *self)
|
||||
{
|
||||
self->priv = ADJUSTMENT_PRIVATE (self);
|
||||
|
||||
self->priv->is_constructing = TRUE;
|
||||
}
|
||||
|
||||
StAdjustment *
|
||||
st_adjustment_new (gdouble value,
|
||||
gdouble lower,
|
||||
gdouble upper,
|
||||
gdouble step_increment,
|
||||
gdouble page_increment,
|
||||
gdouble page_size)
|
||||
{
|
||||
return g_object_new (ST_TYPE_ADJUSTMENT,
|
||||
"value", value,
|
||||
"lower", lower,
|
||||
"upper", upper,
|
||||
"step-increment", step_increment,
|
||||
"page-increment", page_increment,
|
||||
"page-size", page_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
gdouble
|
||||
st_adjustment_get_value (StAdjustment *adjustment)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
if (priv->interpolation)
|
||||
{
|
||||
return MAX (priv->lower,
|
||||
MIN (priv->upper - priv->page_size,
|
||||
priv->new_position));
|
||||
}
|
||||
else
|
||||
return priv->value;
|
||||
}
|
||||
|
||||
void
|
||||
st_adjustment_set_value (StAdjustment *adjustment,
|
||||
gdouble value)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
stop_interpolation (adjustment);
|
||||
|
||||
/* Defer clamp until after construction. */
|
||||
if (!priv->is_constructing)
|
||||
{
|
||||
if (!priv->elastic)
|
||||
value = CLAMP (value,
|
||||
priv->lower,
|
||||
MAX (priv->lower, priv->upper - priv->page_size));
|
||||
}
|
||||
|
||||
if (priv->value != value)
|
||||
{
|
||||
priv->value = value;
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "value");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
st_adjustment_clamp_page (StAdjustment *adjustment,
|
||||
gdouble lower,
|
||||
gdouble upper)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
gboolean changed;
|
||||
|
||||
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
stop_interpolation (adjustment);
|
||||
|
||||
lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
|
||||
upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
|
||||
|
||||
changed = FALSE;
|
||||
|
||||
if (priv->value + priv->page_size > upper)
|
||||
{
|
||||
priv->value = upper - priv->page_size;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->value < lower)
|
||||
{
|
||||
priv->value = lower;
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
g_object_notify (G_OBJECT (adjustment), "value");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_adjustment_set_lower (StAdjustment *adjustment,
|
||||
gdouble lower)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->lower != lower)
|
||||
{
|
||||
priv->lower = lower;
|
||||
|
||||
g_signal_emit (adjustment, signals[CHANGED], 0);
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "lower");
|
||||
|
||||
/* Defer clamp until after construction. */
|
||||
if (!priv->is_constructing)
|
||||
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_adjustment_set_upper (StAdjustment *adjustment,
|
||||
gdouble upper)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->upper != upper)
|
||||
{
|
||||
priv->upper = upper;
|
||||
|
||||
g_signal_emit (adjustment, signals[CHANGED], 0);
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "upper");
|
||||
|
||||
/* Defer clamp until after construction. */
|
||||
if (!priv->is_constructing)
|
||||
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_adjustment_set_step_increment (StAdjustment *adjustment,
|
||||
gdouble step)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->step_increment != step)
|
||||
{
|
||||
priv->step_increment = step;
|
||||
|
||||
g_signal_emit (adjustment, signals[CHANGED], 0);
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "step-increment");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_adjustment_set_page_increment (StAdjustment *adjustment,
|
||||
gdouble page)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->page_increment != page)
|
||||
{
|
||||
priv->page_increment = page;
|
||||
|
||||
g_signal_emit (adjustment, signals[CHANGED], 0);
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "page-increment");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_adjustment_set_page_size (StAdjustment *adjustment,
|
||||
gdouble size)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
if (priv->page_size != size)
|
||||
{
|
||||
priv->page_size = size;
|
||||
|
||||
g_signal_emit (adjustment, signals[CHANGED], 0);
|
||||
|
||||
g_object_notify (G_OBJECT (adjustment), "page_size");
|
||||
|
||||
/* Well explicitely clamp after construction. */
|
||||
if (!priv->is_constructing)
|
||||
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
st_adjustment_set_values (StAdjustment *adjustment,
|
||||
gdouble value,
|
||||
gdouble lower,
|
||||
gdouble upper,
|
||||
gdouble step_increment,
|
||||
gdouble page_increment,
|
||||
gdouble page_size)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
gboolean emit_changed = FALSE;
|
||||
|
||||
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
|
||||
g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
|
||||
g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
|
||||
g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
stop_interpolation (adjustment);
|
||||
|
||||
emit_changed = FALSE;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (adjustment));
|
||||
|
||||
emit_changed |= st_adjustment_set_lower (adjustment, lower);
|
||||
emit_changed |= st_adjustment_set_upper (adjustment, upper);
|
||||
emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
|
||||
emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
|
||||
emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
|
||||
|
||||
if (value != priv->value)
|
||||
{
|
||||
st_adjustment_set_value (adjustment, value);
|
||||
emit_changed = TRUE;
|
||||
}
|
||||
|
||||
if (emit_changed)
|
||||
g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (adjustment));
|
||||
}
|
||||
|
||||
void
|
||||
st_adjustment_get_values (StAdjustment *adjustment,
|
||||
gdouble *value,
|
||||
gdouble *lower,
|
||||
gdouble *upper,
|
||||
gdouble *step_increment,
|
||||
gdouble *page_increment,
|
||||
gdouble *page_size)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
if (lower)
|
||||
*lower = priv->lower;
|
||||
|
||||
if (upper)
|
||||
*upper = priv->upper;
|
||||
|
||||
if (value)
|
||||
*value = st_adjustment_get_value (adjustment);
|
||||
|
||||
if (step_increment)
|
||||
*step_increment = priv->step_increment;
|
||||
|
||||
if (page_increment)
|
||||
*page_increment = priv->page_increment;
|
||||
|
||||
if (page_size)
|
||||
*page_size = priv->page_size;
|
||||
}
|
||||
|
||||
static void
|
||||
interpolation_new_frame_cb (ClutterTimeline *timeline,
|
||||
guint msecs,
|
||||
StAdjustment *adjustment)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
priv->interpolation = NULL;
|
||||
|
||||
if (priv->elastic)
|
||||
{
|
||||
gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) / 1.0;
|
||||
gdouble dx = priv->old_position
|
||||
+ (priv->new_position - priv->old_position)
|
||||
* progress;
|
||||
|
||||
st_adjustment_set_value (adjustment, dx);
|
||||
}
|
||||
else
|
||||
st_adjustment_set_value (adjustment,
|
||||
priv->old_position +
|
||||
(priv->new_position - priv->old_position) *
|
||||
clutter_timeline_get_progress (timeline));
|
||||
|
||||
priv->interpolation = timeline;
|
||||
}
|
||||
|
||||
static void
|
||||
interpolation_completed_cb (ClutterTimeline *timeline,
|
||||
StAdjustment *adjustment)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
stop_interpolation (adjustment);
|
||||
st_adjustment_set_value (adjustment, priv->new_position);
|
||||
}
|
||||
|
||||
/* Note, there's super-optimal code that does a similar thing in
|
||||
* clutter-alpha.c
|
||||
*
|
||||
* Tried this instead of CLUTTER_ALPHA_SINE_INC, but I think SINE_INC looks
|
||||
* better. Leaving code here in case this is revisited.
|
||||
*/
|
||||
/*
|
||||
static guint32
|
||||
bounce_alpha_func (ClutterAlpha *alpha,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterFixed progress, angle;
|
||||
ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
|
||||
|
||||
progress = clutter_timeline_get_progressx (timeline);
|
||||
angle = clutter_qmulx (CFX_PI_2 + CFX_PI_4/2, progress);
|
||||
|
||||
return clutter_sinx (angle) +
|
||||
(CFX_ONE - clutter_sinx (CFX_PI_2 + CFX_PI_4/2));
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
st_adjustment_interpolate (StAdjustment *adjustment,
|
||||
gdouble value,
|
||||
guint duration)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
|
||||
stop_interpolation (adjustment);
|
||||
|
||||
if (duration <= 1)
|
||||
{
|
||||
st_adjustment_set_value (adjustment, value);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->old_position = priv->value;
|
||||
priv->new_position = value;
|
||||
|
||||
priv->interpolation = clutter_timeline_new (duration);
|
||||
|
||||
if (priv->elastic)
|
||||
priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
|
||||
CLUTTER_LINEAR);
|
||||
|
||||
g_signal_connect (priv->interpolation,
|
||||
"new-frame",
|
||||
G_CALLBACK (interpolation_new_frame_cb),
|
||||
adjustment);
|
||||
g_signal_connect (priv->interpolation,
|
||||
"completed",
|
||||
G_CALLBACK (interpolation_completed_cb),
|
||||
adjustment);
|
||||
|
||||
clutter_timeline_start (priv->interpolation);
|
||||
}
|
||||
|
||||
gboolean
|
||||
st_adjustment_get_elastic (StAdjustment *adjustment)
|
||||
{
|
||||
return adjustment->priv->elastic;
|
||||
}
|
||||
|
||||
void
|
||||
st_adjustment_set_elastic (StAdjustment *adjustment,
|
||||
gboolean elastic)
|
||||
{
|
||||
adjustment->priv->elastic = elastic;
|
||||
}
|
||||
|
||||
gboolean
|
||||
st_adjustment_clamp (StAdjustment *adjustment,
|
||||
gboolean interpolate,
|
||||
guint duration)
|
||||
{
|
||||
StAdjustmentPrivate *priv = adjustment->priv;
|
||||
gdouble dest = priv->value;
|
||||
|
||||
if (priv->value < priv->lower)
|
||||
dest = priv->lower;
|
||||
|
||||
if (priv->value > priv->upper - priv->page_size)
|
||||
dest = priv->upper - priv->page_size;
|
||||
|
||||
if (dest != priv->value)
|
||||
{
|
||||
if (interpolate)
|
||||
st_adjustment_interpolate (adjustment, dest, duration);
|
||||
else
|
||||
st_adjustment_set_value (adjustment, dest);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
122
src/st/st-adjustment.h
Normal file
122
src/st/st-adjustment.h
Normal file
@ -0,0 +1,122 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-adjustment.h: Adjustment object
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_ADJUSTMENT_H__
|
||||
#define __ST_ADJUSTMENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_ADJUSTMENT (st_adjustment_get_type())
|
||||
#define ST_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ADJUSTMENT, StAdjustment))
|
||||
#define ST_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ADJUSTMENT))
|
||||
#define ST_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
|
||||
#define ST_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ADJUSTMENT))
|
||||
#define ST_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
|
||||
|
||||
typedef struct _StAdjustment StAdjustment;
|
||||
typedef struct _StAdjustmentPrivate StAdjustmentPrivate;
|
||||
typedef struct _StAdjustmentClass StAdjustmentClass;
|
||||
|
||||
/**
|
||||
* StAdjustment:
|
||||
*
|
||||
* Class for handling an interval between to values. The contents of
|
||||
* the #StAdjustment are private and should be accessed using the
|
||||
* public API.
|
||||
*/
|
||||
struct _StAdjustment
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent_instance;
|
||||
|
||||
StAdjustmentPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* StAdjustmentClass
|
||||
* @changed: Class handler for the ::changed signal.
|
||||
*
|
||||
* Base class for #StAdjustment.
|
||||
*/
|
||||
struct _StAdjustmentClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
void (* changed) (StAdjustment *adjustment);
|
||||
};
|
||||
|
||||
GType st_adjustment_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StAdjustment *st_adjustment_new (gdouble value,
|
||||
gdouble lower,
|
||||
gdouble upper,
|
||||
gdouble step_increment,
|
||||
gdouble page_increment,
|
||||
gdouble page_size);
|
||||
gdouble st_adjustment_get_value (StAdjustment *adjustment);
|
||||
void st_adjustment_set_value (StAdjustment *adjustment,
|
||||
gdouble value);
|
||||
void st_adjustment_clamp_page (StAdjustment *adjustment,
|
||||
gdouble lower,
|
||||
gdouble upper);
|
||||
void st_adjustment_set_values (StAdjustment *adjustment,
|
||||
gdouble value,
|
||||
gdouble lower,
|
||||
gdouble upper,
|
||||
gdouble step_increment,
|
||||
gdouble page_increment,
|
||||
gdouble page_size);
|
||||
void st_adjustment_get_values (StAdjustment *adjustment,
|
||||
gdouble *value,
|
||||
gdouble *lower,
|
||||
gdouble *upper,
|
||||
gdouble *step_increment,
|
||||
gdouble *page_increment,
|
||||
gdouble *page_size);
|
||||
|
||||
void st_adjustment_interpolate (StAdjustment *adjustment,
|
||||
gdouble value,
|
||||
guint duration);
|
||||
|
||||
gboolean st_adjustment_get_elastic (StAdjustment *adjustment);
|
||||
void st_adjustment_set_elastic (StAdjustment *adjustment,
|
||||
gboolean elastic);
|
||||
|
||||
gboolean st_adjustment_clamp (StAdjustment *adjustment,
|
||||
gboolean interpolate,
|
||||
guint duration);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_ADJUSTMENT_H__ */
|
748
src/st/st-bin.c
Normal file
748
src/st/st-bin.c
Normal file
@ -0,0 +1,748 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-bin.c: Basic container actor
|
||||
*
|
||||
* Copyright (c) 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-bin
|
||||
* @short_description: a simple container with one actor
|
||||
*
|
||||
* #StBin is a simple container capable of having only one
|
||||
* #ClutterActor as a child.
|
||||
*
|
||||
* #StBin inherits from #StWidget, so it is fully themable.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-bin.h"
|
||||
#include "st-enum-types.h"
|
||||
#include "st-private.h"
|
||||
|
||||
#define ST_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BIN, StBinPrivate))
|
||||
|
||||
struct _StBinPrivate
|
||||
{
|
||||
ClutterActor *child;
|
||||
|
||||
StAlign x_align;
|
||||
StAlign y_align;
|
||||
|
||||
guint x_fill : 1;
|
||||
guint y_fill : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_CHILD,
|
||||
PROP_X_ALIGN,
|
||||
PROP_Y_ALIGN,
|
||||
PROP_X_FILL,
|
||||
PROP_Y_FILL
|
||||
};
|
||||
|
||||
static void clutter_container_iface_init (ClutterContainerIface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET,
|
||||
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
|
||||
clutter_container_iface_init));
|
||||
|
||||
void
|
||||
_st_bin_get_align_factors (StBin *bin,
|
||||
gdouble *x_align,
|
||||
gdouble *y_align)
|
||||
{
|
||||
StBinPrivate *priv = bin->priv;
|
||||
gdouble factor;
|
||||
|
||||
switch (priv->x_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
factor = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
factor = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
factor = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
factor = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (x_align)
|
||||
*x_align = factor;
|
||||
|
||||
switch (priv->y_align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
factor = 0.0;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_MIDDLE:
|
||||
factor = 0.5;
|
||||
break;
|
||||
|
||||
case ST_ALIGN_END:
|
||||
factor = 1.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
factor = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (y_align)
|
||||
*y_align = factor;
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_add (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
st_bin_set_child (ST_BIN (container), actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_remove (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (container)->priv;
|
||||
|
||||
if (priv->child == actor)
|
||||
st_bin_set_child (ST_BIN (container), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_foreach (ClutterContainer *container,
|
||||
ClutterCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (container)->priv;
|
||||
|
||||
if (priv->child)
|
||||
callback (priv->child, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_container_iface_init (ClutterContainerIface *iface)
|
||||
{
|
||||
iface->add = st_bin_add;
|
||||
iface->remove = st_bin_remove;
|
||||
iface->foreach = st_bin_foreach;
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_paint (ClutterActor *self)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (self)->priv;
|
||||
|
||||
/* allow StWidget to paint the background */
|
||||
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->paint (self);
|
||||
|
||||
/* the pain our child */
|
||||
if (priv->child)
|
||||
clutter_actor_paint (priv->child);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_pick (ClutterActor *self,
|
||||
const ClutterColor *pick_color)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (self)->priv;
|
||||
|
||||
/* get the default pick implementation */
|
||||
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->pick (self, pick_color);
|
||||
|
||||
if (priv->child)
|
||||
clutter_actor_paint (priv->child);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (self)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->allocate (self, box,
|
||||
flags);
|
||||
|
||||
if (priv->child)
|
||||
{
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
gfloat natural_width, natural_height;
|
||||
gfloat min_width, min_height;
|
||||
gfloat child_width, child_height;
|
||||
gfloat available_width, available_height;
|
||||
ClutterRequestMode request;
|
||||
ClutterActorBox content_box;
|
||||
ClutterActorBox allocation = { 0, };
|
||||
gdouble x_align, y_align;
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &content_box);
|
||||
|
||||
_st_bin_get_align_factors (ST_BIN (self), &x_align, &y_align);
|
||||
|
||||
available_width = content_box.x2 - content_box.x1;
|
||||
available_height = content_box.y2 - content_box.y1;
|
||||
|
||||
if (available_width < 0)
|
||||
available_width = 0;
|
||||
|
||||
if (available_height < 0)
|
||||
available_height = 0;
|
||||
|
||||
if (priv->x_fill)
|
||||
{
|
||||
allocation.x1 = (int) content_box.x1;
|
||||
allocation.x2 = (int) content_box.x2;
|
||||
}
|
||||
|
||||
if (priv->y_fill)
|
||||
{
|
||||
allocation.y1 = (int) content_box.y1;
|
||||
allocation.y2 = (int) content_box.y2;
|
||||
}
|
||||
|
||||
/* if we are filling horizontally and vertically then we're done */
|
||||
if (priv->x_fill && priv->y_fill)
|
||||
{
|
||||
clutter_actor_allocate (priv->child, &allocation, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
g_object_get (G_OBJECT (priv->child), "request-mode", &request, NULL);
|
||||
|
||||
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->child, available_height,
|
||||
&min_width,
|
||||
&natural_width);
|
||||
|
||||
child_width = CLAMP (natural_width, min_width, available_width);
|
||||
|
||||
clutter_actor_get_preferred_height (priv->child, child_width,
|
||||
&min_height,
|
||||
&natural_height);
|
||||
|
||||
child_height = CLAMP (natural_height, min_height, available_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_actor_get_preferred_height (priv->child, available_width,
|
||||
&min_height,
|
||||
&natural_height);
|
||||
|
||||
child_height = CLAMP (natural_height, min_height, available_height);
|
||||
|
||||
clutter_actor_get_preferred_width (priv->child, child_height,
|
||||
&min_width,
|
||||
&natural_width);
|
||||
|
||||
child_width = CLAMP (natural_width, min_width, available_width);
|
||||
}
|
||||
|
||||
if (!priv->x_fill)
|
||||
{
|
||||
allocation.x1 = content_box.x1 + (int) ((available_width - child_width) * x_align);
|
||||
allocation.x2 = allocation.x1 + child_width;
|
||||
}
|
||||
|
||||
if (!priv->y_fill)
|
||||
{
|
||||
allocation.y1 = content_box.y1 + (int) ((available_height - child_height) * y_align);
|
||||
allocation.y2 = allocation.y1 + child_height;
|
||||
}
|
||||
|
||||
clutter_actor_allocate (priv->child, &allocation, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
if (priv->child == NULL)
|
||||
{
|
||||
if (min_width_p)
|
||||
*min_width_p = 0;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->child, for_height,
|
||||
min_width_p,
|
||||
natural_width_p);
|
||||
}
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_get_preferred_height (ClutterActor *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
if (priv->child == NULL)
|
||||
{
|
||||
if (min_height_p)
|
||||
*min_height_p = 0;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_actor_get_preferred_height (priv->child, for_width,
|
||||
min_height_p,
|
||||
natural_height_p);
|
||||
}
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_dispose (GObject *gobject)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (gobject)->priv;
|
||||
|
||||
if (priv->child)
|
||||
{
|
||||
clutter_actor_unparent (priv->child);
|
||||
priv->child = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_bin_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StBin *bin = ST_BIN (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CHILD:
|
||||
st_bin_set_child (bin, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_X_ALIGN:
|
||||
st_bin_set_alignment (bin,
|
||||
g_value_get_enum (value),
|
||||
bin->priv->y_align);
|
||||
break;
|
||||
|
||||
case PROP_Y_ALIGN:
|
||||
st_bin_set_alignment (bin,
|
||||
bin->priv->x_align,
|
||||
g_value_get_enum (value));
|
||||
break;
|
||||
|
||||
case PROP_X_FILL:
|
||||
st_bin_set_fill (bin,
|
||||
g_value_get_boolean (value),
|
||||
bin->priv->y_fill);
|
||||
break;
|
||||
|
||||
case PROP_Y_FILL:
|
||||
st_bin_set_fill (bin,
|
||||
bin->priv->y_fill,
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StBinPrivate *priv = ST_BIN (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CHILD:
|
||||
g_value_set_object (value, priv->child);
|
||||
break;
|
||||
|
||||
case PROP_X_FILL:
|
||||
g_value_set_boolean (value, priv->x_fill);
|
||||
break;
|
||||
|
||||
case PROP_Y_FILL:
|
||||
g_value_set_boolean (value, priv->y_fill);
|
||||
break;
|
||||
|
||||
case PROP_X_ALIGN:
|
||||
g_value_set_enum (value, priv->x_align);
|
||||
break;
|
||||
|
||||
case PROP_Y_ALIGN:
|
||||
g_value_set_enum (value, priv->y_align);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_class_init (StBinClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StBinPrivate));
|
||||
|
||||
gobject_class->set_property = st_bin_set_property;
|
||||
gobject_class->get_property = st_bin_get_property;
|
||||
gobject_class->dispose = st_bin_dispose;
|
||||
|
||||
actor_class->get_preferred_width = st_bin_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_bin_get_preferred_height;
|
||||
actor_class->allocate = st_bin_allocate;
|
||||
actor_class->paint = st_bin_paint;
|
||||
actor_class->pick = st_bin_pick;
|
||||
|
||||
/**
|
||||
* StBin:child:
|
||||
*
|
||||
* The child #ClutterActor of the #StBin container.
|
||||
*/
|
||||
pspec = g_param_spec_object ("child",
|
||||
"Child",
|
||||
"The child of the Bin",
|
||||
CLUTTER_TYPE_ACTOR,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
|
||||
|
||||
/**
|
||||
* StBin:x-align:
|
||||
*
|
||||
* The horizontal alignment of the #StBin child.
|
||||
*/
|
||||
pspec = g_param_spec_enum ("x-align",
|
||||
"X Align",
|
||||
"The horizontal alignment",
|
||||
ST_TYPE_ALIGN,
|
||||
ST_ALIGN_MIDDLE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
|
||||
|
||||
/**
|
||||
* StBin:y-align:
|
||||
*
|
||||
* The vertical alignment of the #StBin child.
|
||||
*/
|
||||
pspec = g_param_spec_enum ("y-align",
|
||||
"Y Align",
|
||||
"The vertical alignment",
|
||||
ST_TYPE_ALIGN,
|
||||
ST_ALIGN_MIDDLE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
|
||||
|
||||
/**
|
||||
* StBin:x-fill:
|
||||
*
|
||||
* Whether the child should fill the horizontal allocation
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("x-fill",
|
||||
"X Fill",
|
||||
"Whether the child should fill the "
|
||||
"horizontal allocation",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
|
||||
|
||||
/**
|
||||
* StBin:y-fill:
|
||||
*
|
||||
* Whether the child should fill the vertical allocation
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("y-fill",
|
||||
"Y Fill",
|
||||
"Whether the child should fill the "
|
||||
"vertical allocation",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_bin_init (StBin *bin)
|
||||
{
|
||||
bin->priv = ST_BIN_GET_PRIVATE (bin);
|
||||
|
||||
bin->priv->x_align = ST_ALIGN_MIDDLE;
|
||||
bin->priv->y_align = ST_ALIGN_MIDDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_new:
|
||||
*
|
||||
* Creates a new #StBin, a simple container for one child.
|
||||
*
|
||||
* Return value: the newly created #StBin actor
|
||||
*/
|
||||
StWidget *
|
||||
st_bin_new (void)
|
||||
{
|
||||
return g_object_new (ST_TYPE_BIN, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_set_child:
|
||||
* @bin: a #StBin
|
||||
* @child: a #ClutterActor, or %NULL
|
||||
*
|
||||
* Sets @child as the child of @bin.
|
||||
*
|
||||
* If @bin already has a child, the previous child is removed.
|
||||
*/
|
||||
void
|
||||
st_bin_set_child (StBin *bin,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StBinPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_BIN (bin));
|
||||
g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
|
||||
|
||||
priv = bin->priv;
|
||||
|
||||
if (priv->child == child)
|
||||
return;
|
||||
|
||||
if (priv->child)
|
||||
{
|
||||
ClutterActor *old_child = priv->child;
|
||||
|
||||
g_object_ref (old_child);
|
||||
|
||||
priv->child = NULL;
|
||||
clutter_actor_unparent (old_child);
|
||||
|
||||
g_signal_emit_by_name (bin, "actor-removed", old_child);
|
||||
|
||||
g_object_unref (old_child);
|
||||
}
|
||||
|
||||
if (child)
|
||||
{
|
||||
priv->child = child;
|
||||
clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
|
||||
|
||||
g_signal_emit_by_name (bin, "actor-added", priv->child);
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
|
||||
|
||||
g_object_notify (G_OBJECT (bin), "child");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_get_child:
|
||||
* @bin: a #StBin
|
||||
*
|
||||
* Retrieves a pointer to the child of @bin.
|
||||
*
|
||||
* Return value: (transfer none): a #ClutterActor, or %NULL
|
||||
*/
|
||||
ClutterActor *
|
||||
st_bin_get_child (StBin *bin)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_BIN (bin), NULL);
|
||||
|
||||
return bin->priv->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_set_alignment:
|
||||
* @bin: a #StBin
|
||||
* @x_align: horizontal alignment
|
||||
* @y_align: vertical alignment
|
||||
*
|
||||
* Sets the horizontal and vertical alignment of the child
|
||||
* inside a #StBin.
|
||||
*/
|
||||
void
|
||||
st_bin_set_alignment (StBin *bin,
|
||||
StAlign x_align,
|
||||
StAlign y_align)
|
||||
{
|
||||
StBinPrivate *priv;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
g_return_if_fail (ST_IS_BIN (bin));
|
||||
|
||||
priv = bin->priv;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (bin));
|
||||
|
||||
if (priv->x_align != x_align)
|
||||
{
|
||||
priv->x_align = x_align;
|
||||
g_object_notify (G_OBJECT (bin), "x-align");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->y_align != y_align)
|
||||
{
|
||||
priv->y_align = y_align;
|
||||
g_object_notify (G_OBJECT (bin), "y-align");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (bin));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_get_alignment:
|
||||
* @bin: a #StBin
|
||||
* @x_align: return location for the horizontal alignment, or %NULL
|
||||
* @y_align: return location for the vertical alignment, or %NULL
|
||||
*
|
||||
* Retrieves the horizontal and vertical alignment of the child
|
||||
* inside a #StBin, as set by st_bin_set_alignment().
|
||||
*/
|
||||
void
|
||||
st_bin_get_alignment (StBin *bin,
|
||||
StAlign *x_align,
|
||||
StAlign *y_align)
|
||||
{
|
||||
StBinPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_BIN (bin));
|
||||
|
||||
priv = bin->priv;
|
||||
|
||||
if (x_align)
|
||||
*x_align = priv->x_align;
|
||||
|
||||
if (y_align)
|
||||
*y_align = priv->y_align;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_set_fill:
|
||||
* @bin: a #StBin
|
||||
* @x_fill: %TRUE if the child should fill horizontally the @bin
|
||||
* @y_fill: %TRUE if the child should fill vertically the @bin
|
||||
*
|
||||
* Sets whether the child of @bin should fill out the horizontal
|
||||
* and/or vertical allocation of the parent
|
||||
*/
|
||||
void
|
||||
st_bin_set_fill (StBin *bin,
|
||||
gboolean x_fill,
|
||||
gboolean y_fill)
|
||||
{
|
||||
StBinPrivate *priv;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
g_return_if_fail (ST_IS_BIN (bin));
|
||||
|
||||
priv = bin->priv;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (bin));
|
||||
|
||||
if (priv->x_fill != x_fill)
|
||||
{
|
||||
priv->x_fill = x_fill;
|
||||
changed = TRUE;
|
||||
|
||||
g_object_notify (G_OBJECT (bin), "x-fill");
|
||||
}
|
||||
|
||||
if (priv->y_fill != y_fill)
|
||||
{
|
||||
priv->y_fill = y_fill;
|
||||
changed = TRUE;
|
||||
|
||||
g_object_notify (G_OBJECT (bin), "y-fill");
|
||||
}
|
||||
|
||||
if (changed)
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (bin));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_bin_get_fill:
|
||||
* @bin: a #StBin
|
||||
* @x_fill: (out): return location for the horizontal fill, or %NULL
|
||||
* @y_fill: (out): return location for the vertical fill, or %NULL
|
||||
*
|
||||
* Retrieves the horizontal and vertical fill settings
|
||||
*/
|
||||
void
|
||||
st_bin_get_fill (StBin *bin,
|
||||
gboolean *x_fill,
|
||||
gboolean *y_fill)
|
||||
{
|
||||
g_return_if_fail (ST_IS_BIN (bin));
|
||||
|
||||
if (x_fill)
|
||||
*x_fill = bin->priv->x_fill;
|
||||
|
||||
if (y_fill)
|
||||
*y_fill = bin->priv->y_fill;
|
||||
}
|
93
src/st/st-bin.h
Normal file
93
src/st/st-bin.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-bin.h: Basic container actor
|
||||
*
|
||||
* Copyright 2009, 2008 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_BIN_H__
|
||||
#define __ST_BIN_H__
|
||||
|
||||
#include <st/st-types.h>
|
||||
#include <st/st-widget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_BIN (st_bin_get_type ())
|
||||
#define ST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BIN, StBin))
|
||||
#define ST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BIN))
|
||||
#define ST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BIN, StBinClass))
|
||||
#define ST_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BIN))
|
||||
#define ST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BIN, StBinClass))
|
||||
|
||||
typedef struct _StBin StBin;
|
||||
typedef struct _StBinPrivate StBinPrivate;
|
||||
typedef struct _StBinClass StBinClass;
|
||||
|
||||
/**
|
||||
* StBin:
|
||||
*
|
||||
* The #StBin struct contains only private data
|
||||
*/
|
||||
struct _StBin
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent_instance;
|
||||
|
||||
StBinPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* StBinClass:
|
||||
*
|
||||
* The #StBinClass struct contains only private data
|
||||
*/
|
||||
struct _StBinClass
|
||||
{
|
||||
/*< private >*/
|
||||
StWidgetClass parent_class;
|
||||
};
|
||||
|
||||
GType st_bin_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget * st_bin_new (void);
|
||||
void st_bin_set_child (StBin *bin,
|
||||
ClutterActor *child);
|
||||
ClutterActor *st_bin_get_child (StBin *bin);
|
||||
void st_bin_set_alignment (StBin *bin,
|
||||
StAlign x_align,
|
||||
StAlign y_align);
|
||||
void st_bin_get_alignment (StBin *bin,
|
||||
StAlign *x_align,
|
||||
StAlign *y_align);
|
||||
void st_bin_set_fill (StBin *bin,
|
||||
gboolean x_fill,
|
||||
gboolean y_fill);
|
||||
void st_bin_get_fill (StBin *bin,
|
||||
gboolean *x_fill,
|
||||
gboolean *y_fill);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_BIN_H__ */
|
92
src/st/st-border-image.c
Normal file
92
src/st/st-border-image.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "st-border-image.h"
|
||||
|
||||
struct _StBorderImage {
|
||||
GObject parent;
|
||||
|
||||
char *filename;
|
||||
int border_top;
|
||||
int border_right;
|
||||
int border_bottom;
|
||||
int border_left;
|
||||
};
|
||||
|
||||
struct _StBorderImageClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StBorderImage, st_border_image, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
st_border_image_finalize (GObject *object)
|
||||
{
|
||||
StBorderImage *image = ST_BORDER_IMAGE (object);
|
||||
|
||||
g_free (image->filename);
|
||||
|
||||
G_OBJECT_CLASS (st_border_image_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_border_image_class_init (StBorderImageClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = st_border_image_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
st_border_image_init (StBorderImage *image)
|
||||
{
|
||||
}
|
||||
|
||||
StBorderImage *
|
||||
st_border_image_new (const char *filename,
|
||||
int border_top,
|
||||
int border_right,
|
||||
int border_bottom,
|
||||
int border_left)
|
||||
{
|
||||
StBorderImage *image;
|
||||
|
||||
image = g_object_new (ST_TYPE_BORDER_IMAGE, NULL);
|
||||
|
||||
image->filename = g_strdup (filename);
|
||||
image->border_top = border_top;
|
||||
image->border_right = border_right;
|
||||
image->border_bottom = border_bottom;
|
||||
image->border_left = border_left;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
const char *
|
||||
st_border_image_get_filename (StBorderImage *image)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), NULL);
|
||||
|
||||
return image->filename;
|
||||
}
|
||||
|
||||
void
|
||||
st_border_image_get_borders (StBorderImage *image,
|
||||
int *border_top,
|
||||
int *border_right,
|
||||
int *border_bottom,
|
||||
int *border_left)
|
||||
{
|
||||
g_return_if_fail (ST_IS_BORDER_IMAGE (image));
|
||||
|
||||
if (border_top)
|
||||
*border_top = image->border_top;
|
||||
if (border_right)
|
||||
*border_right = image->border_right;
|
||||
if (border_bottom)
|
||||
*border_bottom = image->border_bottom;
|
||||
if (border_left)
|
||||
*border_left = image->border_left;
|
||||
}
|
38
src/st/st-border-image.h
Normal file
38
src/st/st-border-image.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_BORDER_IMAGE_H__
|
||||
#define __ST_BORDER_IMAGE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* A StBorderImage encapsulates an image with specified unscaled borders on each edge.
|
||||
*/
|
||||
typedef struct _StBorderImage StBorderImage;
|
||||
typedef struct _StBorderImageClass StBorderImageClass;
|
||||
|
||||
#define ST_TYPE_BORDER_IMAGE (st_border_image_get_type ())
|
||||
#define ST_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_BORDER_IMAGE, StBorderImage))
|
||||
#define ST_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BORDER_IMAGE, StBorderImageClass))
|
||||
#define ST_IS_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_BORDER_IMAGE))
|
||||
#define ST_IS_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BORDER_IMAGE))
|
||||
#define ST_BORDER_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BORDER_IMAGE, StBorderImageClass))
|
||||
|
||||
GType st_border_image_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StBorderImage *st_border_image_new (const char *filename,
|
||||
int border_top,
|
||||
int border_right,
|
||||
int border_bottom,
|
||||
int border_left);
|
||||
|
||||
const char *st_border_image_get_filename (StBorderImage *image);
|
||||
void st_border_image_get_borders (StBorderImage *image,
|
||||
int *border_top,
|
||||
int *border_right,
|
||||
int *border_bottom,
|
||||
int *border_left);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_BORDER_IMAGE_H__ */
|
190
src/st/st-box-layout-child.c
Normal file
190
src/st/st-box-layout-child.c
Normal file
@ -0,0 +1,190 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-box-layout-child.c: box layout child actor
|
||||
*
|
||||
* Copyright 2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-box-layout-child
|
||||
* @short_description: meta data associated with a #StBoxLayout child.
|
||||
*
|
||||
* #StBoxLayoutChild is a #ClutterChildMeta implementation that stores the
|
||||
* child properties for children inside a #StBoxLayout.
|
||||
*/
|
||||
|
||||
#include "st-box-layout-child.h"
|
||||
#include "st-private.h"
|
||||
|
||||
G_DEFINE_TYPE (StBoxLayoutChild, st_box_layout_child, CLUTTER_TYPE_CHILD_META)
|
||||
|
||||
#define BOX_LAYOUT_CHILD_PRIVATE(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildPrivate))
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_EXPAND,
|
||||
PROP_X_FILL,
|
||||
PROP_Y_FILL,
|
||||
PROP_X_ALIGN,
|
||||
PROP_Y_ALIGN
|
||||
};
|
||||
|
||||
static void
|
||||
st_box_layout_child_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_EXPAND:
|
||||
g_value_set_boolean (value, child->expand);
|
||||
break;
|
||||
case PROP_X_FILL:
|
||||
g_value_set_boolean (value, child->x_fill);
|
||||
break;
|
||||
case PROP_Y_FILL:
|
||||
g_value_set_boolean (value, child->y_fill);
|
||||
break;
|
||||
case PROP_X_ALIGN:
|
||||
g_value_set_enum (value, child->x_align);
|
||||
break;
|
||||
case PROP_Y_ALIGN:
|
||||
g_value_set_enum (value, child->y_align);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_box_layout_child_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object);
|
||||
StBoxLayout *box = ST_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_EXPAND:
|
||||
child->expand = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_X_FILL:
|
||||
child->x_fill = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_Y_FILL:
|
||||
child->y_fill = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_X_ALIGN:
|
||||
child->x_align = g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_Y_ALIGN:
|
||||
child->y_align = g_value_get_enum (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout ((ClutterActor*) box);
|
||||
}
|
||||
|
||||
static void
|
||||
st_box_layout_child_dispose (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (st_box_layout_child_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_box_layout_child_finalize (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (st_box_layout_child_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_box_layout_child_class_init (StBoxLayoutChildClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
object_class->get_property = st_box_layout_child_get_property;
|
||||
object_class->set_property = st_box_layout_child_set_property;
|
||||
object_class->dispose = st_box_layout_child_dispose;
|
||||
object_class->finalize = st_box_layout_child_finalize;
|
||||
|
||||
|
||||
pspec = g_param_spec_boolean ("expand", "Expand",
|
||||
"Allocate the child extra space",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_EXPAND, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("x-fill", "x-fill",
|
||||
"Whether the child should receive priority "
|
||||
"when the container is allocating spare space "
|
||||
"on the horizontal axis",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_X_FILL, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("y-fill", "y-fill",
|
||||
"Whether the child should receive priority "
|
||||
"when the container is allocating spare space "
|
||||
"on the vertical axis",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_Y_FILL, pspec);
|
||||
|
||||
pspec = g_param_spec_enum ("x-align",
|
||||
"X Alignment",
|
||||
"X alignment of the widget within the cell",
|
||||
ST_TYPE_ALIGN,
|
||||
ST_ALIGN_MIDDLE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_X_ALIGN, pspec);
|
||||
|
||||
pspec = g_param_spec_enum ("y-align",
|
||||
"Y Alignment",
|
||||
"Y alignment of the widget within the cell",
|
||||
ST_TYPE_ALIGN,
|
||||
ST_ALIGN_MIDDLE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_box_layout_child_init (StBoxLayoutChild *self)
|
||||
{
|
||||
self->expand = FALSE;
|
||||
|
||||
self->x_fill = TRUE;
|
||||
self->y_fill = TRUE;
|
||||
|
||||
self->x_align = ST_ALIGN_MIDDLE;
|
||||
self->y_align = ST_ALIGN_MIDDLE;
|
||||
}
|
||||
|
85
src/st/st-box-layout-child.h
Normal file
85
src/st/st-box-layout-child.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-box-layout-child.h: box layout child actor
|
||||
*
|
||||
* Copyright 2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*/
|
||||
|
||||
#ifndef _ST_BOX_LAYOUT_CHILD_H
|
||||
#define _ST_BOX_LAYOUT_CHILD_H
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include "st-enum-types.h"
|
||||
#include "st-box-layout.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_BOX_LAYOUT_CHILD st_box_layout_child_get_type()
|
||||
|
||||
#define ST_BOX_LAYOUT_CHILD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChild))
|
||||
|
||||
#define ST_BOX_LAYOUT_CHILD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass))
|
||||
|
||||
#define ST_IS_BOX_LAYOUT_CHILD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT_CHILD))
|
||||
|
||||
#define ST_IS_BOX_LAYOUT_CHILD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
ST_TYPE_BOX_LAYOUT_CHILD))
|
||||
|
||||
#define ST_BOX_LAYOUT_CHILD_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass))
|
||||
|
||||
typedef struct _StBoxLayoutChild StBoxLayoutChild;
|
||||
typedef struct _StBoxLayoutChildClass StBoxLayoutChildClass;
|
||||
typedef struct _StBoxLayoutChildPrivate StBoxLayoutChildPrivate;
|
||||
|
||||
/**
|
||||
* StBoxLayoutChild:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StBoxLayoutChild
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterChildMeta parent;
|
||||
|
||||
gboolean expand;
|
||||
gboolean x_fill : 1;
|
||||
gboolean y_fill : 1;
|
||||
StAlign x_align;
|
||||
StAlign y_align;
|
||||
};
|
||||
|
||||
struct _StBoxLayoutChildClass
|
||||
{
|
||||
ClutterChildMetaClass parent_class;
|
||||
};
|
||||
|
||||
GType st_box_layout_child_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _ST_BOX_LAYOUT_CHILD_H */
|
1227
src/st/st-box-layout.c
Normal file
1227
src/st/st-box-layout.c
Normal file
File diff suppressed because it is too large
Load Diff
94
src/st/st-box-layout.h
Normal file
94
src/st/st-box-layout.h
Normal file
@ -0,0 +1,94 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-box-layout.h: box layout actor
|
||||
*
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ST_BOX_LAYOUT_H
|
||||
#define _ST_BOX_LAYOUT_H
|
||||
|
||||
#include <st/st-widget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_BOX_LAYOUT st_box_layout_get_type()
|
||||
|
||||
#define ST_BOX_LAYOUT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT, StBoxLayout))
|
||||
|
||||
#define ST_BOX_LAYOUT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
ST_TYPE_BOX_LAYOUT, StBoxLayoutClass))
|
||||
|
||||
#define ST_IS_BOX_LAYOUT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT))
|
||||
|
||||
#define ST_IS_BOX_LAYOUT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
ST_TYPE_BOX_LAYOUT))
|
||||
|
||||
#define ST_BOX_LAYOUT_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
ST_TYPE_BOX_LAYOUT, StBoxLayoutClass))
|
||||
|
||||
typedef struct _StBoxLayout StBoxLayout;
|
||||
typedef struct _StBoxLayoutClass StBoxLayoutClass;
|
||||
typedef struct _StBoxLayoutPrivate StBoxLayoutPrivate;
|
||||
|
||||
/**
|
||||
* StBoxLayout:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StBoxLayout
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent;
|
||||
|
||||
StBoxLayoutPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StBoxLayoutClass
|
||||
{
|
||||
StWidgetClass parent_class;
|
||||
};
|
||||
|
||||
GType st_box_layout_get_type (void);
|
||||
|
||||
StWidget *st_box_layout_new (void);
|
||||
|
||||
void st_box_layout_set_vertical (StBoxLayout *box,
|
||||
gboolean vertical);
|
||||
gboolean st_box_layout_get_vertical (StBoxLayout *box);
|
||||
|
||||
void st_box_layout_set_pack_start (StBoxLayout *box,
|
||||
gboolean pack_start);
|
||||
gboolean st_box_layout_get_pack_start (StBoxLayout *box);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _ST_BOX_LAYOUT_H */
|
692
src/st/st-button.c
Normal file
692
src/st/st-button.c
Normal file
@ -0,0 +1,692 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-button.c: Plain button actor
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
|
||||
* Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-button
|
||||
* @short_description: Button widget
|
||||
*
|
||||
* A button widget with support for either a text label or icon, toggle mode
|
||||
* and transitions effects between states.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-button.h"
|
||||
|
||||
#include "st-marshal.h"
|
||||
#include "st-texture-frame.h"
|
||||
#include "st-texture-cache.h"
|
||||
#include "st-private.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_LABEL,
|
||||
PROP_TOGGLE_MODE,
|
||||
PROP_CHECKED,
|
||||
PROP_TRANSITION_DURATION
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CLICKED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
#define ST_BUTTON_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BUTTON, StButtonPrivate))
|
||||
|
||||
struct _StButtonPrivate
|
||||
{
|
||||
gchar *text;
|
||||
|
||||
ClutterActor *old_bg;
|
||||
gboolean old_bg_parented; /* TRUE if we have adopted old_bg */
|
||||
|
||||
guint8 old_opacity;
|
||||
|
||||
guint is_pressed : 1;
|
||||
guint is_hover : 1;
|
||||
guint is_checked : 1;
|
||||
guint is_toggle : 1;
|
||||
|
||||
gint transition_duration;
|
||||
|
||||
ClutterAnimation *animation;
|
||||
|
||||
gint spacing;
|
||||
};
|
||||
|
||||
static guint button_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
G_DEFINE_TYPE (StButton, st_button, ST_TYPE_BIN);
|
||||
|
||||
static void
|
||||
st_button_update_label_style (StButton *button)
|
||||
{
|
||||
ClutterActor *label;
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string = NULL;
|
||||
|
||||
label = st_bin_get_child ((StBin*) button);
|
||||
|
||||
/* check the child is really a label */
|
||||
if (!CLUTTER_IS_TEXT (label))
|
||||
return;
|
||||
|
||||
theme_node = st_widget_get_theme_node (ST_WIDGET (button));
|
||||
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (label), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (label), font_string);
|
||||
g_free (font_string);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_dispose_old_bg (StButton *button)
|
||||
{
|
||||
StButtonPrivate *priv = button->priv;
|
||||
|
||||
if (priv->old_bg)
|
||||
{
|
||||
if (priv->old_bg_parented)
|
||||
{
|
||||
clutter_actor_unparent (priv->old_bg);
|
||||
priv->old_bg_parented = FALSE;
|
||||
}
|
||||
g_object_unref (priv->old_bg);
|
||||
priv->old_bg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_animation_completed (ClutterAnimation *animation,
|
||||
StButton *button)
|
||||
{
|
||||
st_button_dispose_old_bg (button);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_style_changed (StWidget *widget)
|
||||
{
|
||||
StButton *button = ST_BUTTON (widget);
|
||||
StButtonPrivate *priv = button->priv;
|
||||
StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (button));
|
||||
ClutterActor *bg_image;
|
||||
double spacing;
|
||||
|
||||
st_button_dispose_old_bg (button);
|
||||
|
||||
bg_image = st_widget_get_border_image ((StWidget*) button);
|
||||
if (bg_image)
|
||||
button->priv->old_bg = g_object_ref (bg_image);
|
||||
|
||||
ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
|
||||
|
||||
spacing = 6;
|
||||
st_theme_node_get_length (theme_node, "border-spacing", FALSE, &spacing);
|
||||
priv->spacing = (int)(0.5 + spacing);
|
||||
|
||||
/* update the label styling */
|
||||
st_button_update_label_style (button);
|
||||
|
||||
/* run a transition if applicable */
|
||||
if (button_class->transition)
|
||||
{
|
||||
button_class->transition (button, priv->old_bg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (priv->old_bg &&
|
||||
(!st_widget_get_style_pseudo_class (widget)))
|
||||
{
|
||||
ClutterAnimation *animation;
|
||||
if (!clutter_actor_get_parent (priv->old_bg))
|
||||
{
|
||||
clutter_actor_set_parent (priv->old_bg, (ClutterActor*) widget);
|
||||
priv->old_bg_parented = TRUE;
|
||||
}
|
||||
if (priv->transition_duration > 0)
|
||||
{
|
||||
animation = clutter_actor_animate (priv->old_bg,
|
||||
CLUTTER_LINEAR,
|
||||
priv->transition_duration,
|
||||
"opacity", 0,
|
||||
NULL);
|
||||
g_signal_connect (animation, "completed",
|
||||
G_CALLBACK (st_animation_completed), button);
|
||||
}
|
||||
else
|
||||
{
|
||||
st_button_dispose_old_bg (button);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_real_pressed (StButton *button)
|
||||
{
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "active");
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_real_released (StButton *button)
|
||||
{
|
||||
StButtonPrivate *priv = button->priv;
|
||||
|
||||
if (priv->is_checked)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
|
||||
else if (!priv->is_hover)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
|
||||
else
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_button_button_press (ClutterActor *actor,
|
||||
ClutterButtonEvent *event)
|
||||
{
|
||||
st_widget_hide_tooltip (ST_WIDGET (actor));
|
||||
|
||||
if (event->button == 1)
|
||||
{
|
||||
StButton *button = ST_BUTTON (actor);
|
||||
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
|
||||
|
||||
button->priv->is_pressed = TRUE;
|
||||
|
||||
clutter_grab_pointer (actor);
|
||||
|
||||
if (klass->pressed)
|
||||
klass->pressed (button);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_button_button_release (ClutterActor *actor,
|
||||
ClutterButtonEvent *event)
|
||||
{
|
||||
if (event->button == 1)
|
||||
{
|
||||
StButton *button = ST_BUTTON (actor);
|
||||
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
|
||||
|
||||
if (!button->priv->is_pressed)
|
||||
return FALSE;
|
||||
|
||||
clutter_ungrab_pointer ();
|
||||
|
||||
if (button->priv->is_toggle)
|
||||
{
|
||||
st_button_set_checked (button, !button->priv->is_checked);
|
||||
}
|
||||
|
||||
button->priv->is_pressed = FALSE;
|
||||
|
||||
if (klass->released)
|
||||
klass->released (button);
|
||||
|
||||
g_signal_emit (button, button_signals[CLICKED], 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_button_enter (ClutterActor *actor,
|
||||
ClutterCrossingEvent *event)
|
||||
{
|
||||
StButton *button = ST_BUTTON (actor);
|
||||
|
||||
if (!button->priv->is_checked)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
|
||||
|
||||
button->priv->is_hover = 1;
|
||||
|
||||
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_button_leave (ClutterActor *actor,
|
||||
ClutterCrossingEvent *event)
|
||||
{
|
||||
StButton *button = ST_BUTTON (actor);
|
||||
|
||||
button->priv->is_hover = 0;
|
||||
|
||||
if (button->priv->is_pressed)
|
||||
{
|
||||
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
|
||||
|
||||
clutter_ungrab_pointer ();
|
||||
|
||||
button->priv->is_pressed = FALSE;
|
||||
|
||||
if (klass->released)
|
||||
klass->released (button);
|
||||
}
|
||||
|
||||
if (button->priv->is_checked)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
|
||||
else
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
|
||||
|
||||
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StButton *button = ST_BUTTON (gobject);
|
||||
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
st_button_set_label (button, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_TOGGLE_MODE:
|
||||
st_button_set_toggle_mode (button, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_CHECKED:
|
||||
st_button_set_checked (button, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_TRANSITION_DURATION:
|
||||
priv->transition_duration = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
g_value_set_string (value, priv->text);
|
||||
break;
|
||||
case PROP_TOGGLE_MODE:
|
||||
g_value_set_boolean (value, priv->is_toggle);
|
||||
break;
|
||||
case PROP_CHECKED:
|
||||
g_value_set_boolean (value, priv->is_checked);
|
||||
break;
|
||||
case PROP_TRANSITION_DURATION:
|
||||
g_value_set_int (value, priv->transition_duration);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_finalize (GObject *gobject)
|
||||
{
|
||||
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
|
||||
|
||||
g_free (priv->text);
|
||||
|
||||
G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_dispose (GObject *gobject)
|
||||
{
|
||||
st_button_dispose_old_bg (ST_BUTTON (gobject));
|
||||
|
||||
G_OBJECT_CLASS (st_button_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_map (ClutterActor *self)
|
||||
{
|
||||
StButtonPrivate *priv = ST_BUTTON (self)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_button_parent_class)->map (self);
|
||||
|
||||
if (priv->old_bg && priv->old_bg_parented)
|
||||
clutter_actor_map (priv->old_bg);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_unmap (ClutterActor *self)
|
||||
{
|
||||
StButtonPrivate *priv = ST_BUTTON (self)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_button_parent_class)->unmap (self);
|
||||
|
||||
if (priv->old_bg && priv->old_bg_parented)
|
||||
clutter_actor_unmap (priv->old_bg);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_draw_background (StWidget *widget)
|
||||
{
|
||||
StButtonPrivate *priv;
|
||||
|
||||
ST_WIDGET_CLASS (st_button_parent_class)->draw_background (widget);
|
||||
|
||||
priv = ST_BUTTON (widget)->priv;
|
||||
|
||||
if (priv->old_bg && priv->old_bg_parented)
|
||||
clutter_actor_paint (priv->old_bg);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_class_init (StButtonClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StButtonPrivate));
|
||||
|
||||
klass->pressed = st_button_real_pressed;
|
||||
klass->released = st_button_real_released;
|
||||
|
||||
gobject_class->set_property = st_button_set_property;
|
||||
gobject_class->get_property = st_button_get_property;
|
||||
gobject_class->dispose = st_button_dispose;
|
||||
gobject_class->finalize = st_button_finalize;
|
||||
|
||||
actor_class->button_press_event = st_button_button_press;
|
||||
actor_class->button_release_event = st_button_button_release;
|
||||
actor_class->enter_event = st_button_enter;
|
||||
actor_class->leave_event = st_button_leave;
|
||||
|
||||
actor_class->map = st_button_map;
|
||||
actor_class->unmap = st_button_unmap;
|
||||
|
||||
widget_class->draw_background = st_button_draw_background;
|
||||
widget_class->style_changed = st_button_style_changed;
|
||||
|
||||
pspec = g_param_spec_string ("label",
|
||||
"Label",
|
||||
"Label of the button",
|
||||
NULL, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("toggle-mode",
|
||||
"Toggle Mode",
|
||||
"Enable or disable toggling",
|
||||
FALSE, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TOGGLE_MODE, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("checked",
|
||||
"Checked",
|
||||
"Indicates if a toggle button is \"on\""
|
||||
" or \"off\"",
|
||||
FALSE, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_CHECKED, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("transition-duration",
|
||||
"Transition Duration",
|
||||
"Duration of the state transition effect",
|
||||
0, G_MAXINT, 120, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TRANSITION_DURATION, pspec);
|
||||
|
||||
|
||||
/**
|
||||
* StButton::clicked:
|
||||
* @button: the object that received the signal
|
||||
*
|
||||
* Emitted when the user activates the button, either with a mouse press and
|
||||
* release or with the keyboard.
|
||||
*/
|
||||
|
||||
button_signals[CLICKED] =
|
||||
g_signal_new ("clicked",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StButtonClass, clicked),
|
||||
NULL, NULL,
|
||||
_st_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
st_button_init (StButton *button)
|
||||
{
|
||||
button->priv = ST_BUTTON_GET_PRIVATE (button);
|
||||
button->priv->transition_duration = 120;
|
||||
button->priv->spacing = 6;
|
||||
|
||||
clutter_actor_set_reactive ((ClutterActor *) button, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_new:
|
||||
*
|
||||
* Create a new button
|
||||
*
|
||||
* Returns: a new #StButton
|
||||
*/
|
||||
StWidget *
|
||||
st_button_new (void)
|
||||
{
|
||||
return g_object_new (ST_TYPE_BUTTON, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_new_with_label:
|
||||
* @text: text to set the label to
|
||||
*
|
||||
* Create a new #StButton with the specified label
|
||||
*
|
||||
* Returns: a new #StButton
|
||||
*/
|
||||
StWidget *
|
||||
st_button_new_with_label (const gchar *text)
|
||||
{
|
||||
return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_get_label:
|
||||
* @button: a #StButton
|
||||
*
|
||||
* Get the text displayed on the button
|
||||
*
|
||||
* Returns: the text for the button. This must not be freed by the application
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
st_button_get_label (StButton *button)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
|
||||
|
||||
return button->priv->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_set_label:
|
||||
* @button: a #Stbutton
|
||||
* @text: text to set the label to
|
||||
*
|
||||
* Sets the text displayed on the button
|
||||
*/
|
||||
void
|
||||
st_button_set_label (StButton *button,
|
||||
const gchar *text)
|
||||
{
|
||||
StButtonPrivate *priv;
|
||||
ClutterActor *label;
|
||||
|
||||
g_return_if_fail (ST_IS_BUTTON (button));
|
||||
|
||||
priv = button->priv;
|
||||
|
||||
g_free (priv->text);
|
||||
|
||||
if (text)
|
||||
priv->text = g_strdup (text);
|
||||
else
|
||||
priv->text = g_strdup ("");
|
||||
|
||||
label = st_bin_get_child ((StBin*) button);
|
||||
|
||||
if (label && CLUTTER_IS_TEXT (label))
|
||||
{
|
||||
clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
|
||||
}
|
||||
else
|
||||
{
|
||||
label = g_object_new (CLUTTER_TYPE_TEXT,
|
||||
"text", priv->text,
|
||||
"line-alignment", PANGO_ALIGN_CENTER,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
"use-markup", TRUE,
|
||||
NULL);
|
||||
st_bin_set_child ((StBin*) button, label);
|
||||
}
|
||||
|
||||
/* Fake a style change so that we reset the style properties on the label */
|
||||
st_widget_style_changed (ST_WIDGET (button));
|
||||
|
||||
g_object_notify (G_OBJECT (button), "label");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_get_toggle_mode:
|
||||
* @button: a #StButton
|
||||
*
|
||||
* Get the toggle mode status of the button.
|
||||
*
|
||||
* Returns: #TRUE if toggle mode is set, otherwise #FALSE
|
||||
*/
|
||||
gboolean
|
||||
st_button_get_toggle_mode (StButton *button)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
|
||||
|
||||
return button->priv->is_toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_set_toggle_mode:
|
||||
* @button: a #Stbutton
|
||||
* @toggle: #TRUE or #FALSE
|
||||
*
|
||||
* Enables or disables toggle mode for the button. In toggle mode, the active
|
||||
* state will be "toggled" when the user clicks the button.
|
||||
*/
|
||||
void
|
||||
st_button_set_toggle_mode (StButton *button,
|
||||
gboolean toggle)
|
||||
{
|
||||
g_return_if_fail (ST_IS_BUTTON (button));
|
||||
|
||||
button->priv->is_toggle = toggle;
|
||||
|
||||
g_object_notify (G_OBJECT (button), "toggle-mode");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_get_checked:
|
||||
* @button: a #StButton
|
||||
*
|
||||
* Get the state of the button that is in toggle mode.
|
||||
*
|
||||
* Returns: #TRUE if the button is checked, or #FALSE if not
|
||||
*/
|
||||
gboolean
|
||||
st_button_get_checked (StButton *button)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
|
||||
|
||||
return button->priv->is_checked;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_button_set_checked:
|
||||
* @button: a #Stbutton
|
||||
* @checked: #TRUE or #FALSE
|
||||
*
|
||||
* Sets the pressed state of the button. This is only really useful if the
|
||||
* button has #toggle-mode mode set to #TRUE.
|
||||
*/
|
||||
void
|
||||
st_button_set_checked (StButton *button,
|
||||
gboolean checked)
|
||||
{
|
||||
g_return_if_fail (ST_IS_BUTTON (button));
|
||||
|
||||
if (button->priv->is_checked != checked)
|
||||
{
|
||||
button->priv->is_checked = checked;
|
||||
|
||||
if (checked)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
|
||||
else
|
||||
if (button->priv->is_hover)
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
|
||||
else
|
||||
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (button), "checked");
|
||||
}
|
94
src/st/st-button.h
Normal file
94
src/st/st-button.h
Normal file
@ -0,0 +1,94 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-button.h: Plain button actor
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
|
||||
* Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_BUTTON_H__
|
||||
#define __ST_BUTTON_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <st/st-bin.h>
|
||||
|
||||
#define ST_TYPE_BUTTON (st_button_get_type ())
|
||||
#define ST_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BUTTON, StButton))
|
||||
#define ST_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BUTTON))
|
||||
#define ST_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BUTTON, StButtonClass))
|
||||
#define ST_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BUTTON))
|
||||
#define ST_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BUTTON, StButtonClass))
|
||||
|
||||
typedef struct _StButton StButton;
|
||||
typedef struct _StButtonPrivate StButtonPrivate;
|
||||
typedef struct _StButtonClass StButtonClass;
|
||||
|
||||
/**
|
||||
* StButton:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
|
||||
struct _StButton
|
||||
{
|
||||
/*< private >*/
|
||||
StBin parent_instance;
|
||||
|
||||
StButtonPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StButtonClass
|
||||
{
|
||||
StBinClass parent_class;
|
||||
|
||||
/* vfuncs, not signals */
|
||||
void (* pressed) (StButton *button);
|
||||
void (* released) (StButton *button);
|
||||
void (* transition) (StButton *button,
|
||||
ClutterActor *old_bg);
|
||||
|
||||
/* signals */
|
||||
void (* clicked) (StButton *button);
|
||||
};
|
||||
|
||||
GType st_button_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget * st_button_new (void);
|
||||
StWidget * st_button_new_with_label (const gchar *text);
|
||||
G_CONST_RETURN gchar *st_button_get_label (StButton *button);
|
||||
void st_button_set_label (StButton *button,
|
||||
const gchar *text);
|
||||
void st_button_set_toggle_mode (StButton *button,
|
||||
gboolean toggle);
|
||||
gboolean st_button_get_toggle_mode (StButton *button);
|
||||
void st_button_set_checked (StButton *button,
|
||||
gboolean checked);
|
||||
gboolean st_button_get_checked (StButton *button);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_BUTTON_H__ */
|
381
src/st/st-clipboard.c
Normal file
381
src/st/st-clipboard.c
Normal file
@ -0,0 +1,381 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-clipboard.c: clipboard object
|
||||
*
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-clipboard
|
||||
* @short_description: a simple representation of the X clipboard
|
||||
*
|
||||
* #StCliboard is a very simple object representation of the clipboard
|
||||
* available to applications. Text is always assumed to be UTF-8 and non-text
|
||||
* items are not handled.
|
||||
*/
|
||||
|
||||
|
||||
#include "st-clipboard.h"
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <string.h>
|
||||
|
||||
G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT)
|
||||
|
||||
#define CLIPBOARD_PRIVATE(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_CLIPBOARD, StClipboardPrivate))
|
||||
|
||||
struct _StClipboardPrivate
|
||||
{
|
||||
Window clipboard_window;
|
||||
gchar *clipboard_text;
|
||||
|
||||
Atom *supported_targets;
|
||||
gint n_targets;
|
||||
};
|
||||
|
||||
typedef struct _EventFilterData EventFilterData;
|
||||
struct _EventFilterData
|
||||
{
|
||||
StClipboard *clipboard;
|
||||
StClipboardCallbackFunc callback;
|
||||
gpointer user_data;
|
||||
};
|
||||
|
||||
static Atom __atom_clip = None;
|
||||
static Atom __utf8_string = None;
|
||||
static Atom __atom_targets = None;
|
||||
|
||||
static void
|
||||
st_clipboard_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_clipboard_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_clipboard_dispose (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (st_clipboard_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_clipboard_finalize (GObject *object)
|
||||
{
|
||||
StClipboardPrivate *priv = ((StClipboard *) object)->priv;
|
||||
|
||||
g_free (priv->clipboard_text);
|
||||
priv->clipboard_text = NULL;
|
||||
|
||||
g_free (priv->supported_targets);
|
||||
priv->supported_targets = NULL;
|
||||
priv->n_targets = 0;
|
||||
|
||||
G_OBJECT_CLASS (st_clipboard_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static ClutterX11FilterReturn
|
||||
st_clipboard_provider (XEvent *xev,
|
||||
ClutterEvent *cev,
|
||||
StClipboard *clipboard)
|
||||
{
|
||||
XSelectionEvent notify_event;
|
||||
XSelectionRequestEvent *req_event;
|
||||
|
||||
if (xev->type != SelectionRequest)
|
||||
return CLUTTER_X11_FILTER_CONTINUE;
|
||||
|
||||
req_event = &xev->xselectionrequest;
|
||||
|
||||
clutter_x11_trap_x_errors ();
|
||||
|
||||
if (req_event->target == __atom_targets)
|
||||
{
|
||||
XChangeProperty (req_event->display,
|
||||
req_event->requestor,
|
||||
req_event->property,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(guchar*) clipboard->priv->supported_targets,
|
||||
clipboard->priv->n_targets);
|
||||
}
|
||||
else
|
||||
{
|
||||
XChangeProperty (req_event->display,
|
||||
req_event->requestor,
|
||||
req_event->property,
|
||||
req_event->target,
|
||||
8,
|
||||
PropModeReplace,
|
||||
(guchar*) clipboard->priv->clipboard_text,
|
||||
strlen (clipboard->priv->clipboard_text));
|
||||
}
|
||||
|
||||
notify_event.type = SelectionNotify;
|
||||
notify_event.display = req_event->display;
|
||||
notify_event.requestor = req_event->requestor;
|
||||
notify_event.selection = req_event->selection;
|
||||
notify_event.target = req_event->target;
|
||||
notify_event.time = req_event->time;
|
||||
|
||||
if (req_event->property == None)
|
||||
notify_event.property = req_event->target;
|
||||
else
|
||||
notify_event.property = req_event->property;
|
||||
|
||||
/* notify the requestor that they have a copy of the selection */
|
||||
XSendEvent (req_event->display, req_event->requestor, False, 0,
|
||||
(XEvent *) ¬ify_event);
|
||||
/* Make it happen non async */
|
||||
XSync (clutter_x11_get_default_display(), FALSE);
|
||||
|
||||
clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */
|
||||
|
||||
return CLUTTER_X11_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
st_clipboard_class_init (StClipboardClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StClipboardPrivate));
|
||||
|
||||
object_class->get_property = st_clipboard_get_property;
|
||||
object_class->set_property = st_clipboard_set_property;
|
||||
object_class->dispose = st_clipboard_dispose;
|
||||
object_class->finalize = st_clipboard_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
st_clipboard_init (StClipboard *self)
|
||||
{
|
||||
Display *dpy;
|
||||
StClipboardPrivate *priv;
|
||||
|
||||
priv = self->priv = CLIPBOARD_PRIVATE (self);
|
||||
|
||||
priv->clipboard_window =
|
||||
XCreateSimpleWindow (clutter_x11_get_default_display (),
|
||||
clutter_x11_get_root_window (),
|
||||
-1, -1, 1, 1, 0, 0, 0);
|
||||
|
||||
dpy = clutter_x11_get_default_display ();
|
||||
|
||||
/* Only create once */
|
||||
if (__atom_clip == None)
|
||||
__atom_clip = XInternAtom (dpy, "CLIPBOARD", 0);
|
||||
|
||||
if (__utf8_string == None)
|
||||
__utf8_string = XInternAtom (dpy, "UTF8_STRING", 0);
|
||||
|
||||
if (__atom_targets == None)
|
||||
__atom_targets = XInternAtom (dpy, "TARGETS", 0);
|
||||
|
||||
priv->n_targets = 2;
|
||||
priv->supported_targets = g_new (Atom, priv->n_targets);
|
||||
|
||||
priv->supported_targets[0] = __utf8_string;
|
||||
priv->supported_targets[1] = __atom_targets;
|
||||
|
||||
clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_provider,
|
||||
self);
|
||||
}
|
||||
|
||||
static ClutterX11FilterReturn
|
||||
st_clipboard_x11_event_filter (XEvent *xev,
|
||||
ClutterEvent *cev,
|
||||
EventFilterData *filter_data)
|
||||
{
|
||||
Atom actual_type;
|
||||
int actual_format, result;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *data = NULL;
|
||||
|
||||
if(xev->type != SelectionNotify)
|
||||
return CLUTTER_X11_FILTER_CONTINUE;
|
||||
|
||||
if (xev->xselection.property == None)
|
||||
{
|
||||
/* clipboard empty */
|
||||
filter_data->callback (filter_data->clipboard,
|
||||
NULL,
|
||||
filter_data->user_data);
|
||||
|
||||
clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
|
||||
filter_data);
|
||||
g_free (filter_data);
|
||||
return CLUTTER_X11_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
clutter_x11_trap_x_errors ();
|
||||
|
||||
result = XGetWindowProperty (xev->xselection.display,
|
||||
xev->xselection.requestor,
|
||||
xev->xselection.property,
|
||||
0L, G_MAXINT,
|
||||
True,
|
||||
AnyPropertyType,
|
||||
&actual_type,
|
||||
&actual_format,
|
||||
&nitems,
|
||||
&bytes_after,
|
||||
&data);
|
||||
|
||||
if (clutter_x11_untrap_x_errors () || result != Success)
|
||||
{
|
||||
/* FIXME: handle failure better */
|
||||
g_warning ("Clipboard: prop retrival failed");
|
||||
}
|
||||
|
||||
filter_data->callback (filter_data->clipboard, (char*) data,
|
||||
filter_data->user_data);
|
||||
|
||||
clutter_x11_remove_filter
|
||||
((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
|
||||
filter_data);
|
||||
|
||||
g_free (filter_data);
|
||||
|
||||
if (data)
|
||||
XFree (data);
|
||||
|
||||
return CLUTTER_X11_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_clipboard_get_default:
|
||||
*
|
||||
* Get the global #StClipboard object that represents the clipboard.
|
||||
*
|
||||
* Returns: (transfer none): a #StClipboard owned by St and must not be
|
||||
* unrefferenced or freed.
|
||||
*/
|
||||
StClipboard*
|
||||
st_clipboard_get_default (void)
|
||||
{
|
||||
static StClipboard *default_clipboard = NULL;
|
||||
|
||||
if (!default_clipboard)
|
||||
{
|
||||
default_clipboard = g_object_new (ST_TYPE_CLIPBOARD, NULL);
|
||||
}
|
||||
|
||||
return default_clipboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_clipboard_get_text:
|
||||
* @clipboard: A #StCliboard
|
||||
* @callback: function to be called when the text is retreived
|
||||
* @user_data: data to be passed to the callback
|
||||
*
|
||||
* Request the data from the clipboard in text form. @callback is executed
|
||||
* when the data is retreived.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_clipboard_get_text (StClipboard *clipboard,
|
||||
StClipboardCallbackFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
EventFilterData *data;
|
||||
|
||||
Display *dpy;
|
||||
|
||||
g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
|
||||
g_return_if_fail (callback != NULL);
|
||||
|
||||
data = g_new0 (EventFilterData, 1);
|
||||
data->clipboard = clipboard;
|
||||
data->callback = callback;
|
||||
data->user_data = user_data;
|
||||
|
||||
clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
|
||||
data);
|
||||
|
||||
dpy = clutter_x11_get_default_display ();
|
||||
|
||||
clutter_x11_trap_x_errors (); /* safety on */
|
||||
|
||||
XConvertSelection (dpy,
|
||||
__atom_clip,
|
||||
__utf8_string, __utf8_string,
|
||||
clipboard->priv->clipboard_window,
|
||||
CurrentTime);
|
||||
|
||||
clutter_x11_untrap_x_errors ();
|
||||
}
|
||||
|
||||
/**
|
||||
* st_clipboard_set_text:
|
||||
* @clipboard: A #StClipboard
|
||||
* @text: text to copy to the clipboard
|
||||
*
|
||||
* Sets text as the current contents of the clipboard.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_clipboard_set_text (StClipboard *clipboard,
|
||||
const gchar *text)
|
||||
{
|
||||
StClipboardPrivate *priv;
|
||||
Display *dpy;
|
||||
|
||||
g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
|
||||
g_return_if_fail (text != NULL);
|
||||
|
||||
priv = clipboard->priv;
|
||||
|
||||
/* make a copy of the text */
|
||||
g_free (priv->clipboard_text);
|
||||
priv->clipboard_text = g_strdup (text);
|
||||
|
||||
/* tell X we own the clipboard selection */
|
||||
dpy = clutter_x11_get_default_display ();
|
||||
|
||||
clutter_x11_trap_x_errors ();
|
||||
|
||||
XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime);
|
||||
XSync (dpy, FALSE);
|
||||
|
||||
clutter_x11_untrap_x_errors ();
|
||||
}
|
103
src/st/st-clipboard.h
Normal file
103
src/st/st-clipboard.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-clipboard.h: clipboard object
|
||||
*
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ST_CLIPBOARD_H
|
||||
#define _ST_CLIPBOARD_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_CLIPBOARD st_clipboard_get_type()
|
||||
|
||||
#define ST_CLIPBOARD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
ST_TYPE_CLIPBOARD, StClipboard))
|
||||
|
||||
#define ST_CLIPBOARD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
ST_TYPE_CLIPBOARD, StClipboardClass))
|
||||
|
||||
#define ST_IS_CLIPBOARD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
ST_TYPE_CLIPBOARD))
|
||||
|
||||
#define ST_IS_CLIPBOARD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
ST_TYPE_CLIPBOARD))
|
||||
|
||||
#define ST_CLIPBOARD_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
ST_TYPE_CLIPBOARD, StClipboardClass))
|
||||
|
||||
typedef struct _StClipboard StClipboard;
|
||||
typedef struct _StClipboardClass StClipboardClass;
|
||||
typedef struct _StClipboardPrivate StClipboardPrivate;
|
||||
|
||||
/**
|
||||
* StClipboard:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
struct _StClipboard
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent;
|
||||
StClipboardPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StClipboardClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* StClipboardCallbackFunc:
|
||||
* @clipboard: A #StClipboard
|
||||
* @text: text from the clipboard
|
||||
* @user_data: user data
|
||||
*
|
||||
* Callback function called when text is retrieved from the clipboard.
|
||||
*/
|
||||
typedef void (*StClipboardCallbackFunc) (StClipboard *clipboard,
|
||||
const gchar *text,
|
||||
gpointer user_data);
|
||||
|
||||
GType st_clipboard_get_type (void);
|
||||
|
||||
StClipboard* st_clipboard_get_default (void);
|
||||
|
||||
void st_clipboard_get_text (StClipboard *clipboard,
|
||||
StClipboardCallbackFunc callback,
|
||||
gpointer user_data);
|
||||
void st_clipboard_set_text (StClipboard *clipboard,
|
||||
const gchar *text);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _ST_CLIPBOARD_H */
|
899
src/st/st-entry.c
Normal file
899
src/st/st-entry.c
Normal file
@ -0,0 +1,899 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-entry.c: Plain entry actor
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-entry
|
||||
* @short_description: Widget for displaying text
|
||||
*
|
||||
* #StEntry is a simple widget for displaying text. It derives from
|
||||
* #StWidget to add extra style and placement functionality over
|
||||
* #ClutterText. The internal #ClutterText is publicly accessibly to allow
|
||||
* applications to set further properties.
|
||||
*
|
||||
* #StEntry supports the following pseudo style states:
|
||||
* <itemizedlist>
|
||||
* <listitem>
|
||||
* <para>focus: the widget has focus</para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>indeterminate: the widget is showing the hint text</para>
|
||||
* </listitem>
|
||||
* </itemizedlist>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-entry.h"
|
||||
|
||||
#include "st-im-text.h"
|
||||
#include "st-widget.h"
|
||||
#include "st-texture-cache.h"
|
||||
#include "st-marshal.h"
|
||||
#include "st-clipboard.h"
|
||||
|
||||
#define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor)
|
||||
|
||||
|
||||
/* properties */
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_CLUTTER_TEXT,
|
||||
PROP_HINT_TEXT,
|
||||
PROP_TEXT,
|
||||
};
|
||||
|
||||
/* signals */
|
||||
enum
|
||||
{
|
||||
PRIMARY_ICON_CLICKED,
|
||||
SECONDARY_ICON_CLICKED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
#define ST_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_ENTRY, StEntryPrivate))
|
||||
#define ST_ENTRY_PRIV(x) ((StEntry *) x)->priv
|
||||
|
||||
|
||||
struct _StEntryPrivate
|
||||
{
|
||||
ClutterActor *entry;
|
||||
gchar *hint;
|
||||
|
||||
ClutterActor *primary_icon;
|
||||
ClutterActor *secondary_icon;
|
||||
|
||||
gfloat spacing;
|
||||
};
|
||||
|
||||
static guint entry_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
G_DEFINE_TYPE (StEntry, st_entry, ST_TYPE_WIDGET);
|
||||
|
||||
static void
|
||||
st_entry_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StEntry *entry = ST_ENTRY (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HINT_TEXT:
|
||||
st_entry_set_hint_text (entry, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_TEXT:
|
||||
st_entry_set_text (entry, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CLUTTER_TEXT:
|
||||
g_value_set_object (value, priv->entry);
|
||||
break;
|
||||
|
||||
case PROP_HINT_TEXT:
|
||||
g_value_set_string (value, priv->hint);
|
||||
break;
|
||||
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry)));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_dispose (GObject *object)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (object);
|
||||
|
||||
if (priv->entry)
|
||||
{
|
||||
clutter_actor_unparent (priv->entry);
|
||||
priv->entry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_finalize (GObject *object)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (object);
|
||||
|
||||
g_free (priv->hint);
|
||||
priv->hint = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_style_changed (StWidget *self)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (self);
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string;
|
||||
|
||||
theme_node = st_widget_get_theme_node (self);
|
||||
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (priv->entry), &color);
|
||||
|
||||
if (st_theme_node_get_color (theme_node, "caret-color", FALSE, &color))
|
||||
clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color);
|
||||
|
||||
if (st_theme_node_get_color (theme_node, "selection-background-color", FALSE, &color))
|
||||
clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (priv->entry), font_string);
|
||||
g_free (font_string);
|
||||
|
||||
ST_WIDGET_CLASS (st_entry_parent_class)->style_changed (self);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
gfloat icon_w;
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
clutter_actor_get_preferred_width (priv->entry, for_height,
|
||||
min_width_p,
|
||||
natural_width_p);
|
||||
|
||||
if (priv->primary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w);
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p += icon_w + priv->spacing;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p += icon_w + priv->spacing;
|
||||
}
|
||||
|
||||
if (priv->secondary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->secondary_icon,
|
||||
-1, NULL, &icon_w);
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p += icon_w + priv->spacing;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p += icon_w + priv->spacing;
|
||||
}
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
gfloat icon_h;
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
clutter_actor_get_preferred_height (priv->entry, for_width,
|
||||
min_height_p,
|
||||
natural_height_p);
|
||||
|
||||
if (priv->primary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_height (priv->primary_icon,
|
||||
-1, NULL, &icon_h);
|
||||
|
||||
if (min_height_p && icon_h > *min_height_p)
|
||||
*min_height_p = icon_h;
|
||||
|
||||
if (natural_height_p && icon_h > *natural_height_p)
|
||||
*natural_height_p = icon_h;
|
||||
}
|
||||
|
||||
if (priv->secondary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_height (priv->secondary_icon,
|
||||
-1, NULL, &icon_h);
|
||||
|
||||
if (min_height_p && icon_h > *min_height_p)
|
||||
*min_height_p = icon_h;
|
||||
|
||||
if (natural_height_p && icon_h > *natural_height_p)
|
||||
*natural_height_p = icon_h;
|
||||
}
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
ClutterActorClass *parent_class;
|
||||
ClutterActorBox content_box, child_box, icon_box;
|
||||
gfloat icon_w, icon_h;
|
||||
gfloat entry_h, min_h, pref_h, avail_h;
|
||||
|
||||
parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class);
|
||||
parent_class->allocate (actor, box, flags);
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &content_box);
|
||||
|
||||
avail_h = content_box.y2 - content_box.y1;
|
||||
|
||||
child_box.x1 = content_box.x1;
|
||||
child_box.x2 = content_box.x2;
|
||||
|
||||
if (priv->primary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->primary_icon,
|
||||
-1, NULL, &icon_w);
|
||||
clutter_actor_get_preferred_height (priv->primary_icon,
|
||||
-1, NULL, &icon_h);
|
||||
|
||||
icon_box.x1 = content_box.x1;
|
||||
icon_box.x2 = icon_box.x1 + icon_w;
|
||||
|
||||
icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
|
||||
icon_box.y2 = icon_box.y1 + icon_h;
|
||||
|
||||
clutter_actor_allocate (priv->primary_icon,
|
||||
&icon_box,
|
||||
flags);
|
||||
|
||||
/* reduce the size for the entry */
|
||||
child_box.x1 += icon_w + priv->spacing;
|
||||
}
|
||||
|
||||
if (priv->secondary_icon)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->secondary_icon,
|
||||
-1, NULL, &icon_w);
|
||||
clutter_actor_get_preferred_height (priv->secondary_icon,
|
||||
-1, NULL, &icon_h);
|
||||
|
||||
icon_box.x2 = content_box.x2;
|
||||
icon_box.x1 = icon_box.x2 - icon_w;
|
||||
|
||||
icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
|
||||
icon_box.y2 = icon_box.y1 + icon_h;
|
||||
|
||||
clutter_actor_allocate (priv->secondary_icon,
|
||||
&icon_box,
|
||||
flags);
|
||||
|
||||
/* reduce the size for the entry */
|
||||
child_box.x2 -= icon_w - priv->spacing;
|
||||
}
|
||||
|
||||
clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
|
||||
&min_h, &pref_h);
|
||||
|
||||
entry_h = CLAMP (pref_h, min_h, avail_h);
|
||||
|
||||
child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2);
|
||||
child_box.y2 = child_box.y1 + entry_h;
|
||||
|
||||
clutter_actor_allocate (priv->entry, &child_box, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_text_focus_in_cb (ClutterText *text,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
|
||||
/* remove the hint if visible */
|
||||
if (priv->hint
|
||||
&& !strcmp (clutter_text_get_text (text), priv->hint))
|
||||
{
|
||||
clutter_text_set_text (text, "");
|
||||
}
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (actor), "focus");
|
||||
clutter_text_set_cursor_visible (text, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_text_focus_out_cb (ClutterText *text,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
|
||||
/* add a hint if the entry is empty */
|
||||
if (priv->hint && !strcmp (clutter_text_get_text (text), ""))
|
||||
{
|
||||
clutter_text_set_text (text, priv->hint);
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
|
||||
}
|
||||
else
|
||||
{
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (actor), NULL);
|
||||
}
|
||||
clutter_text_set_cursor_visible (text, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_paint (ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
ClutterActorClass *parent_class;
|
||||
|
||||
parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class);
|
||||
parent_class->paint (actor);
|
||||
|
||||
clutter_actor_paint (priv->entry);
|
||||
|
||||
if (priv->primary_icon)
|
||||
clutter_actor_paint (priv->primary_icon);
|
||||
|
||||
if (priv->secondary_icon)
|
||||
clutter_actor_paint (priv->secondary_icon);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_pick (ClutterActor *actor,
|
||||
const ClutterColor *c)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->pick (actor, c);
|
||||
|
||||
clutter_actor_paint (priv->entry);
|
||||
|
||||
if (priv->primary_icon)
|
||||
clutter_actor_paint (priv->primary_icon);
|
||||
|
||||
if (priv->secondary_icon)
|
||||
clutter_actor_paint (priv->secondary_icon);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_map (ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY (actor)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->map (actor);
|
||||
|
||||
clutter_actor_map (priv->entry);
|
||||
|
||||
if (priv->primary_icon)
|
||||
clutter_actor_map (priv->primary_icon);
|
||||
|
||||
if (priv->secondary_icon)
|
||||
clutter_actor_map (priv->secondary_icon);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_unmap (ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY (actor)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->unmap (actor);
|
||||
|
||||
clutter_actor_unmap (priv->entry);
|
||||
|
||||
if (priv->primary_icon)
|
||||
clutter_actor_unmap (priv->primary_icon);
|
||||
|
||||
if (priv->secondary_icon)
|
||||
clutter_actor_unmap (priv->secondary_icon);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_clipboard_callback (StClipboard *clipboard,
|
||||
const gchar *text,
|
||||
gpointer data)
|
||||
{
|
||||
ClutterText *ctext = (ClutterText*)((StEntry *) data)->priv->entry;
|
||||
gint cursor_pos;
|
||||
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
/* delete the current selection before pasting */
|
||||
clutter_text_delete_selection (ctext);
|
||||
|
||||
/* "paste" the clipboard text into the entry */
|
||||
cursor_pos = clutter_text_get_cursor_position (ctext);
|
||||
clutter_text_insert_text (ctext, text, cursor_pos);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_entry_key_press_event (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
|
||||
/* This is expected to handle events that were emitted for the inner
|
||||
ClutterText. They only reach this function if the ClutterText
|
||||
didn't handle them */
|
||||
|
||||
/* paste */
|
||||
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
|
||||
&& event->keyval == CLUTTER_v)
|
||||
{
|
||||
StClipboard *clipboard;
|
||||
|
||||
clipboard = st_clipboard_get_default ();
|
||||
|
||||
st_clipboard_get_text (clipboard, st_entry_clipboard_callback, actor);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* copy */
|
||||
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
|
||||
&& event->keyval == CLUTTER_c)
|
||||
{
|
||||
StClipboard *clipboard;
|
||||
gchar *text;
|
||||
|
||||
clipboard = st_clipboard_get_default ();
|
||||
|
||||
text = clutter_text_get_selection ((ClutterText*) priv->entry);
|
||||
|
||||
if (text && strlen (text))
|
||||
st_clipboard_set_text (clipboard, text);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* cut */
|
||||
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
|
||||
&& event->keyval == CLUTTER_x)
|
||||
{
|
||||
StClipboard *clipboard;
|
||||
gchar *text;
|
||||
|
||||
clipboard = st_clipboard_get_default ();
|
||||
|
||||
text = clutter_text_get_selection ((ClutterText*) priv->entry);
|
||||
|
||||
if (text && strlen (text))
|
||||
{
|
||||
st_clipboard_set_text (clipboard, text);
|
||||
|
||||
/* now delete the text */
|
||||
clutter_text_delete_selection ((ClutterText *) priv->entry);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_key_focus_in (ClutterActor *actor)
|
||||
{
|
||||
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
|
||||
|
||||
/* We never want key focus. The ClutterText should be given first
|
||||
pass for all key events */
|
||||
clutter_actor_grab_key_focus (priv->entry);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_class_init (StEntryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StEntryPrivate));
|
||||
|
||||
gobject_class->set_property = st_entry_set_property;
|
||||
gobject_class->get_property = st_entry_get_property;
|
||||
gobject_class->finalize = st_entry_finalize;
|
||||
gobject_class->dispose = st_entry_dispose;
|
||||
|
||||
actor_class->get_preferred_width = st_entry_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_entry_get_preferred_height;
|
||||
actor_class->allocate = st_entry_allocate;
|
||||
actor_class->paint = st_entry_paint;
|
||||
actor_class->pick = st_entry_pick;
|
||||
actor_class->map = st_entry_map;
|
||||
actor_class->unmap = st_entry_unmap;
|
||||
|
||||
actor_class->key_press_event = st_entry_key_press_event;
|
||||
actor_class->key_focus_in = st_entry_key_focus_in;
|
||||
|
||||
widget_class->style_changed = st_entry_style_changed;
|
||||
|
||||
pspec = g_param_spec_object ("clutter-text",
|
||||
"Clutter Text",
|
||||
"Internal ClutterText actor",
|
||||
CLUTTER_TYPE_TEXT,
|
||||
G_PARAM_READABLE);
|
||||
g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
|
||||
|
||||
pspec = g_param_spec_string ("hint-text",
|
||||
"Hint Text",
|
||||
"Text to display when the entry is not focused "
|
||||
"and the text property is empty",
|
||||
NULL, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec);
|
||||
|
||||
pspec = g_param_spec_string ("text",
|
||||
"Text",
|
||||
"Text of the entry",
|
||||
NULL, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
|
||||
|
||||
/* signals */
|
||||
/**
|
||||
* StEntry::primary-icon-clicked:
|
||||
*
|
||||
* Emitted when the primary icon is clicked
|
||||
*/
|
||||
entry_signals[PRIMARY_ICON_CLICKED] =
|
||||
g_signal_new ("primary-icon-clicked",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StEntryClass, primary_icon_clicked),
|
||||
NULL, NULL,
|
||||
_st_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
/**
|
||||
* StEntry::secondary-icon-clicked:
|
||||
*
|
||||
* Emitted when the secondary icon is clicked
|
||||
*/
|
||||
entry_signals[SECONDARY_ICON_CLICKED] =
|
||||
g_signal_new ("secondary-icon-clicked",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StEntryClass, secondary_icon_clicked),
|
||||
NULL, NULL,
|
||||
_st_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
st_entry_init (StEntry *entry)
|
||||
{
|
||||
StEntryPrivate *priv;
|
||||
|
||||
priv = entry->priv = ST_ENTRY_GET_PRIVATE (entry);
|
||||
|
||||
priv->entry = g_object_new (ST_TYPE_IM_TEXT,
|
||||
"line-alignment", PANGO_ALIGN_LEFT,
|
||||
"editable", TRUE,
|
||||
"reactive", TRUE,
|
||||
"single-line-mode", TRUE,
|
||||
NULL);
|
||||
|
||||
g_signal_connect (priv->entry, "key-focus-in",
|
||||
G_CALLBACK (clutter_text_focus_in_cb), entry);
|
||||
|
||||
g_signal_connect (priv->entry, "key-focus-out",
|
||||
G_CALLBACK (clutter_text_focus_out_cb), entry);
|
||||
|
||||
priv->spacing = 6.0f;
|
||||
|
||||
clutter_actor_set_parent (priv->entry, CLUTTER_ACTOR (entry));
|
||||
clutter_actor_set_reactive ((ClutterActor *) entry, TRUE);
|
||||
|
||||
/* set cursor hidden until we receive focus */
|
||||
clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_new:
|
||||
* @text: text to set the entry to
|
||||
*
|
||||
* Create a new #StEntry with the specified entry
|
||||
*
|
||||
* Returns: a new #StEntry
|
||||
*/
|
||||
StWidget *
|
||||
st_entry_new (const gchar *text)
|
||||
{
|
||||
StWidget *entry;
|
||||
|
||||
/* add the entry to the stage, but don't allow it to be visible */
|
||||
entry = g_object_new (ST_TYPE_ENTRY,
|
||||
"text", text,
|
||||
NULL);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_get_text:
|
||||
* @entry: a #StEntry
|
||||
*
|
||||
* Get the text displayed on the entry
|
||||
*
|
||||
* Returns: the text for the entry. This must not be freed by the application
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
st_entry_get_text (StEntry *entry)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
|
||||
|
||||
return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_set_text:
|
||||
* @entry: a #StEntry
|
||||
* @text: text to set the entry to
|
||||
*
|
||||
* Sets the text displayed on the entry
|
||||
*/
|
||||
void
|
||||
st_entry_set_text (StEntry *entry,
|
||||
const gchar *text)
|
||||
{
|
||||
StEntryPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ENTRY (entry));
|
||||
|
||||
priv = entry->priv;
|
||||
|
||||
/* set a hint if we are blanking the entry */
|
||||
if (priv->hint
|
||||
&& text && !strcmp ("", text)
|
||||
&& !HAS_FOCUS (priv->entry))
|
||||
{
|
||||
text = priv->hint;
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HAS_FOCUS (priv->entry))
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "focus");
|
||||
else
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (entry), NULL);
|
||||
}
|
||||
|
||||
clutter_text_set_text (CLUTTER_TEXT (priv->entry), text);
|
||||
|
||||
g_object_notify (G_OBJECT (entry), "text");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_get_clutter_text:
|
||||
* @entry: a #StEntry
|
||||
*
|
||||
* Retrieve the internal #ClutterText so that extra parameters can be set
|
||||
*
|
||||
* Returns: (transfer none): the #ClutterText used by #StEntry. The entry is
|
||||
* owned by the #StEntry and should not be unref'ed by the application.
|
||||
*/
|
||||
ClutterActor*
|
||||
st_entry_get_clutter_text (StEntry *entry)
|
||||
{
|
||||
g_return_val_if_fail (ST_ENTRY (entry), NULL);
|
||||
|
||||
return entry->priv->entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_set_hint_text:
|
||||
* @entry: a #StEntry
|
||||
* @text: text to set as the entry hint
|
||||
*
|
||||
* Sets the text to display when the entry is empty and unfocused. When the
|
||||
* entry is displaying the hint, it has a pseudo class of "indeterminate".
|
||||
* A value of NULL unsets the hint.
|
||||
*/
|
||||
void
|
||||
st_entry_set_hint_text (StEntry *entry,
|
||||
const gchar *text)
|
||||
{
|
||||
StEntryPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ENTRY (entry));
|
||||
|
||||
priv = entry->priv;
|
||||
|
||||
g_free (priv->hint);
|
||||
|
||||
priv->hint = g_strdup (text);
|
||||
|
||||
if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), ""))
|
||||
{
|
||||
clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint);
|
||||
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_get_hint_text:
|
||||
* @entry: a #StEntry
|
||||
*
|
||||
* Gets the text that is displayed when the entry is empty and unfocused
|
||||
*
|
||||
* Returns: the current value of the hint property. This string is owned by the
|
||||
* #StEntry and should not be freed or modified.
|
||||
*/
|
||||
G_CONST_RETURN
|
||||
gchar *
|
||||
st_entry_get_hint_text (StEntry *entry)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
|
||||
|
||||
return entry->priv->hint;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_st_entry_icon_press_cb (ClutterActor *actor,
|
||||
ClutterButtonEvent *event,
|
||||
StEntry *entry)
|
||||
{
|
||||
StEntryPrivate *priv = entry->priv;
|
||||
|
||||
if (actor == priv->primary_icon)
|
||||
g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0);
|
||||
else
|
||||
g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_st_entry_set_icon_from_file (StEntry *entry,
|
||||
ClutterActor **icon,
|
||||
const gchar *filename)
|
||||
{
|
||||
if (*icon)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (*icon,
|
||||
_st_entry_icon_press_cb,
|
||||
entry);
|
||||
clutter_actor_unparent (*icon);
|
||||
*icon = NULL;
|
||||
}
|
||||
|
||||
if (filename)
|
||||
{
|
||||
StTextureCache *cache;
|
||||
|
||||
cache = st_texture_cache_get_default ();
|
||||
|
||||
|
||||
|
||||
*icon = (ClutterActor*) st_texture_cache_get_texture (cache, filename);
|
||||
|
||||
clutter_actor_set_reactive (*icon, TRUE);
|
||||
clutter_actor_set_parent (*icon, CLUTTER_ACTOR (entry));
|
||||
g_signal_connect (*icon, "button-release-event",
|
||||
G_CALLBACK (_st_entry_icon_press_cb), entry);
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_set_primary_icon_from_file:
|
||||
* @entry: a #StEntry
|
||||
* @filename: filename of an icon
|
||||
*
|
||||
* Set the primary icon of the entry to the given filename
|
||||
*/
|
||||
void
|
||||
st_entry_set_primary_icon_from_file (StEntry *entry,
|
||||
const gchar *filename)
|
||||
{
|
||||
StEntryPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ENTRY (entry));
|
||||
|
||||
priv = entry->priv;
|
||||
|
||||
_st_entry_set_icon_from_file (entry, &priv->primary_icon, filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* st_entry_set_secondary_icon_from_file:
|
||||
* @entry: a #StEntry
|
||||
* @filename: filename of an icon
|
||||
*
|
||||
* Set the primary icon of the entry to the given filename
|
||||
*/
|
||||
void
|
||||
st_entry_set_secondary_icon_from_file (StEntry *entry,
|
||||
const gchar *filename)
|
||||
{
|
||||
StEntryPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_ENTRY (entry));
|
||||
|
||||
priv = entry->priv;
|
||||
|
||||
_st_entry_set_icon_from_file (entry, &priv->secondary_icon, filename);
|
||||
|
||||
}
|
||||
|
89
src/st/st-entry.h
Normal file
89
src/st/st-entry.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-entry.h: Plain entry actor
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_ENTRY_H__
|
||||
#define __ST_ENTRY_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <st/st-widget.h>
|
||||
|
||||
#define ST_TYPE_ENTRY (st_entry_get_type ())
|
||||
#define ST_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ENTRY, StEntry))
|
||||
#define ST_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ENTRY))
|
||||
#define ST_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ENTRY, StEntryClass))
|
||||
#define ST_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ENTRY))
|
||||
#define ST_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ENTRY, StEntryClass))
|
||||
|
||||
typedef struct _StEntry StEntry;
|
||||
typedef struct _StEntryPrivate StEntryPrivate;
|
||||
typedef struct _StEntryClass StEntryClass;
|
||||
|
||||
/**
|
||||
* StEntry:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
struct _StEntry
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent_instance;
|
||||
|
||||
StEntryPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StEntryClass
|
||||
{
|
||||
StWidgetClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*primary_icon_clicked) (StEntry *entry);
|
||||
void (*secondary_icon_clicked) (StEntry *entry);
|
||||
};
|
||||
|
||||
GType st_entry_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget * st_entry_new (const gchar *text);
|
||||
G_CONST_RETURN gchar *st_entry_get_text (StEntry *entry);
|
||||
void st_entry_set_text (StEntry *entry,
|
||||
const gchar *text);
|
||||
ClutterActor* st_entry_get_clutter_text (StEntry *entry);
|
||||
|
||||
void st_entry_set_hint_text (StEntry *entry,
|
||||
const gchar *text);
|
||||
G_CONST_RETURN gchar *st_entry_get_hint_text (StEntry *entry);
|
||||
|
||||
void st_entry_set_primary_icon_from_file (StEntry *entry,
|
||||
const gchar *filename);
|
||||
void st_entry_set_secondary_icon_from_file (StEntry *entry,
|
||||
const gchar *filename);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_ENTRY_H__ */
|
30
src/st/st-enum-types.c.in
Normal file
30
src/st/st-enum-types.c.in
Normal file
@ -0,0 +1,30 @@
|
||||
/*** BEGIN file-header ***/
|
||||
#include "st-enum-types.h"
|
||||
/*** END file-header ***/
|
||||
|
||||
/*** BEGIN file-production ***/
|
||||
/* enumerations from "@filename@" */
|
||||
#include "@filename@"
|
||||
/*** END file-production ***/
|
||||
|
||||
/*** BEGIN value-header ***/
|
||||
GType
|
||||
@enum_name@_get_type(void) {
|
||||
static GType enum_type_id = 0;
|
||||
if (G_UNLIKELY (!enum_type_id))
|
||||
{
|
||||
static const G@Type@Value values[] = {
|
||||
/*** END value-header ***/
|
||||
|
||||
/*** BEGIN value-production ***/
|
||||
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
|
||||
/*** END value-production ***/
|
||||
|
||||
/*** BEGIN value-tail ***/
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
|
||||
}
|
||||
return enum_type_id;
|
||||
}
|
||||
/*** END value-tail ***/
|
29
src/st/st-enum-types.h.in
Normal file
29
src/st/st-enum-types.h.in
Normal file
@ -0,0 +1,29 @@
|
||||
/*** BEGIN file-header ***/
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_ENUM_TYPES_H__
|
||||
#define __ST_ENUM_TYPES_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/*** END file-header ***/
|
||||
|
||||
/*** BEGIN file-production ***/
|
||||
/* enumerations from "@filename@" */
|
||||
/*** END file-production ***/
|
||||
|
||||
/*** BEGIN file-tail ***/
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* !__ST_ENUM_TYPES_H__ */
|
||||
/*** END file-tail ***/
|
||||
|
||||
/*** BEGIN value-header ***/
|
||||
GType @enum_name@_get_type (void) G_GNUC_CONST;
|
||||
#define ST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
|
||||
|
||||
/*** END value-header ***/
|
477
src/st/st-im-text.c
Normal file
477
src/st/st-im-text.c
Normal file
@ -0,0 +1,477 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-im-text.c
|
||||
*
|
||||
* This started as a copy of ClutterIMText converted to use
|
||||
* GtkIMContext rather than ClutterIMContext. Original code:
|
||||
*
|
||||
* Author: raymond liu <raymond.liu@intel.com>
|
||||
*
|
||||
* Copyright (C) 2009, Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that 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 library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:StIMText
|
||||
* @short_description: Text widget with input method support
|
||||
* @stability: Unstable
|
||||
* @see_also: #ClutterText
|
||||
* @include: st-imtext/st-imtext.h
|
||||
*
|
||||
* #StIMText derives from ClutterText and hooks up better text input
|
||||
* via #GtkIMContext. It is meant to be a drop-in replacement for
|
||||
* ClutterIMText but using GtkIMContext rather than ClutterIMContext.
|
||||
*/
|
||||
|
||||
/* Places where this actor doesn't support all of GtkIMContext:
|
||||
*
|
||||
* A) It doesn't support preedit. This makes it fairly useless for
|
||||
* most complicated input methods. Fixing this requires support
|
||||
* directly in ClutterText, since there is no way to wedge a
|
||||
* preedit string in externally.
|
||||
* B) It doesn't support surrounding context via the
|
||||
* :retrieve-surrounding and :delete-surrounding signals. This could
|
||||
* be added here, but only affects a small number of input methods
|
||||
* and really doesn't make a lot of sense without A)
|
||||
*
|
||||
* Another problem that will show up with usage in GNOME Shell's overview
|
||||
* is that the user may have trouble seeing and interacting with ancilliary
|
||||
* windows shown by the IM.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <X11/extensions/XKB.h>
|
||||
|
||||
#include "st-im-text.h"
|
||||
|
||||
#define ST_IM_TEXT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_IM_TEXT, StIMTextPrivate))
|
||||
|
||||
struct _StIMTextPrivate
|
||||
{
|
||||
GtkIMContext *im_context;
|
||||
GdkWindow *window;
|
||||
|
||||
guint need_im_reset : 1;
|
||||
};
|
||||
|
||||
static void st_im_text_commit_cb (GtkIMContext *context,
|
||||
const gchar *str,
|
||||
StIMText *imtext);
|
||||
|
||||
G_DEFINE_TYPE (StIMText, st_im_text, CLUTTER_TYPE_TEXT)
|
||||
|
||||
static void
|
||||
st_im_text_dispose (GObject *object)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (object)->priv;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (priv->im_context,
|
||||
(void *) st_im_text_commit_cb,
|
||||
object);
|
||||
|
||||
g_object_unref (priv->im_context);
|
||||
priv->im_context = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
update_im_cursor_location (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (self);
|
||||
ClutterActor *parent;
|
||||
gint position;
|
||||
gfloat cursor_x, cursor_y, cursor_height;
|
||||
gfloat actor_x, actor_y;
|
||||
GdkRectangle area;
|
||||
|
||||
position = clutter_text_get_cursor_position (clutter_text);
|
||||
clutter_text_position_to_coords (clutter_text, position,
|
||||
&cursor_x, &cursor_y, &cursor_height);
|
||||
|
||||
/* This is a workaround for a bug in Clutter where
|
||||
* clutter_actor_get_transformed_position doesn't work during
|
||||
* clutter_actor_paint() because the actor has already set up
|
||||
* a model-view matrix.
|
||||
*
|
||||
* http://bugzilla.openedhand.com/show_bug.cgi?id=1115
|
||||
*/
|
||||
actor_x = actor_y = 0.;
|
||||
parent = CLUTTER_ACTOR (self);
|
||||
while (parent)
|
||||
{
|
||||
gfloat x, y;
|
||||
|
||||
clutter_actor_get_position (parent, &x, &y);
|
||||
actor_x += x;
|
||||
actor_y += y;
|
||||
|
||||
parent = clutter_actor_get_parent (parent);
|
||||
}
|
||||
|
||||
area.x = (int)(0.5 + cursor_x + actor_x);
|
||||
area.y = (int)(0.5 + cursor_y + actor_y);
|
||||
area.width = 0;
|
||||
area.height = (int)(0.5 + cursor_height);
|
||||
|
||||
gtk_im_context_set_cursor_location (priv->im_context, &area);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_commit_cb (GtkIMContext *context,
|
||||
const gchar *str,
|
||||
StIMText *imtext)
|
||||
{
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (imtext);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
clutter_text_delete_selection (clutter_text);
|
||||
clutter_text_insert_text (clutter_text, str,
|
||||
clutter_text_get_cursor_position (clutter_text));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_im_context (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
|
||||
if (priv->need_im_reset)
|
||||
{
|
||||
gtk_im_context_reset (priv->im_context);
|
||||
priv->need_im_reset = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_paint (ClutterActor *actor)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
/* This updates the cursor position as a side-effect */
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
update_im_cursor_location (self);
|
||||
}
|
||||
|
||||
/* Returns a new reference to window */
|
||||
static GdkWindow *
|
||||
window_for_actor (ClutterActor *actor)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
ClutterActor *stage;
|
||||
Window xwindow;
|
||||
GdkWindow *window;
|
||||
|
||||
stage = clutter_actor_get_stage (actor);
|
||||
xwindow = clutter_x11_get_stage_window ((ClutterStage *)stage);
|
||||
|
||||
window = gdk_window_lookup_for_display (display, xwindow);
|
||||
if (window)
|
||||
g_object_ref (window);
|
||||
else
|
||||
window = gdk_window_foreign_new_for_display (display, xwindow);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_realize (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
|
||||
priv->window = window_for_actor (actor);
|
||||
gtk_im_context_set_client_window (priv->im_context, priv->window);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_unrealize (ClutterActor *actor)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
|
||||
reset_im_context (self);
|
||||
gtk_im_context_set_client_window (priv->im_context, NULL);
|
||||
g_object_unref (priv->window);
|
||||
priv->window = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_is_modifier (guint16 keyval)
|
||||
{
|
||||
/* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this
|
||||
* really should be implemented */
|
||||
|
||||
switch (keyval)
|
||||
{
|
||||
case GDK_Shift_L:
|
||||
case GDK_Shift_R:
|
||||
case GDK_Control_L:
|
||||
case GDK_Control_R:
|
||||
case GDK_Caps_Lock:
|
||||
case GDK_Shift_Lock:
|
||||
case GDK_Meta_L:
|
||||
case GDK_Meta_R:
|
||||
case GDK_Alt_L:
|
||||
case GDK_Alt_R:
|
||||
case GDK_Super_L:
|
||||
case GDK_Super_R:
|
||||
case GDK_Hyper_L:
|
||||
case GDK_Hyper_R:
|
||||
case GDK_ISO_Lock:
|
||||
case GDK_ISO_Level2_Latch:
|
||||
case GDK_ISO_Level3_Shift:
|
||||
case GDK_ISO_Level3_Latch:
|
||||
case GDK_ISO_Level3_Lock:
|
||||
case GDK_ISO_Level5_Shift:
|
||||
case GDK_ISO_Level5_Latch:
|
||||
case GDK_ISO_Level5_Lock:
|
||||
case GDK_ISO_Group_Shift:
|
||||
case GDK_ISO_Group_Latch:
|
||||
case GDK_ISO_Group_Lock:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GdkEventKey *
|
||||
key_event_to_gdk (ClutterKeyEvent *event_clutter)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
GdkKeymap *keymap = gdk_keymap_get_for_display (display);
|
||||
GdkEventKey *event_gdk;
|
||||
event_gdk = (GdkEventKey *)gdk_event_new ((event_clutter->type == CLUTTER_KEY_PRESS) ?
|
||||
GDK_KEY_PRESS : GDK_KEY_RELEASE);
|
||||
|
||||
event_gdk->window = window_for_actor ((ClutterActor *)event_clutter->stage);
|
||||
event_gdk->send_event = FALSE;
|
||||
event_gdk->time = event_clutter->time;
|
||||
/* This depends on ClutterModifierType and GdkModifierType being
|
||||
* identical, which they are currently. (They both match the X
|
||||
* modifier state in the low 16-bits and have the same extensions.) */
|
||||
event_gdk->state = event_clutter->modifier_state;
|
||||
event_gdk->keyval = event_clutter->keyval;
|
||||
event_gdk->hardware_keycode = event_clutter->hardware_keycode;
|
||||
/* For non-proper non-XKB support, we'd need a huge cut-and-paste
|
||||
* from gdkkeys-x11.c; this is a macro that just shifts a few bits
|
||||
* out of state, so won't make the situation worse if the server
|
||||
* doesn't support XKB; we'll just end up with group == 0 */
|
||||
event_gdk->group = XkbGroupForCoreState (event_gdk->state);
|
||||
|
||||
gdk_keymap_translate_keyboard_state (keymap, event_gdk->hardware_keycode,
|
||||
event_gdk->state, event_gdk->group,
|
||||
&event_gdk->keyval, NULL, NULL, NULL);
|
||||
|
||||
if (event_clutter->unicode_value)
|
||||
{
|
||||
/* This is not particularly close to what GDK does - event_gdk->string
|
||||
* is supposed to be in the locale encoding, and have control keys
|
||||
* as control characters, etc. See gdkevents-x11.c:translate_key_event().
|
||||
* Hopefully no input method is using event.string.
|
||||
*/
|
||||
char buf[6];
|
||||
|
||||
event_gdk->length = g_unichar_to_utf8 (event_clutter->unicode_value, buf);
|
||||
event_gdk->string = g_strndup (buf, event_gdk->length);
|
||||
}
|
||||
|
||||
event_gdk->is_modifier = key_is_modifier (event_gdk->keyval);
|
||||
|
||||
return event_gdk;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_button_press_event (ClutterActor *actor,
|
||||
ClutterButtonEvent *event)
|
||||
{
|
||||
/* The button press indicates the user moving the cursor, or selecting
|
||||
* etc, so we should abort any current preedit operation. ClutterText
|
||||
* treats all buttons identically, so so do we.
|
||||
*/
|
||||
reset_im_context (ST_IM_TEXT (actor));
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event)
|
||||
return CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event (actor, event);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_key_press_event (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
gboolean result = FALSE;
|
||||
int old_position;
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
GdkEventKey *event_gdk = key_event_to_gdk (event);
|
||||
|
||||
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
gdk_event_free ((GdkEvent *)event_gdk);
|
||||
}
|
||||
|
||||
/* ClutterText:position isn't properly notified, so we have to
|
||||
* check before/after to catch a keypress (like an arrow key)
|
||||
* moving the cursor position, which should reset the IM context.
|
||||
* (Resetting on notify::position would require a sentinel when
|
||||
* committing text)
|
||||
*
|
||||
* http://bugzilla.openedhand.com/show_bug.cgi?id=1830
|
||||
*/
|
||||
old_position = clutter_text_get_cursor_position (clutter_text);
|
||||
|
||||
if (!result &&
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event)
|
||||
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event (actor, event);
|
||||
|
||||
if (clutter_text_get_cursor_position (clutter_text) != old_position)
|
||||
reset_im_context (self);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_key_release_event (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
GdkEventKey *event_gdk;
|
||||
gboolean result = FALSE;
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
event_gdk = key_event_to_gdk (event);
|
||||
|
||||
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
gdk_event_free ((GdkEvent *)event_gdk);
|
||||
}
|
||||
|
||||
if (!result &&
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event)
|
||||
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event (actor, event);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_key_focus_in (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
gtk_im_context_focus_in (priv->im_context);
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in (actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_key_focus_out (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
gtk_im_context_focus_out (priv->im_context);
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out (actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_class_init (StIMTextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StIMTextPrivate));
|
||||
|
||||
object_class->dispose = st_im_text_dispose;
|
||||
|
||||
actor_class->paint = st_im_text_paint;
|
||||
actor_class->realize = st_im_text_realize;
|
||||
actor_class->unrealize = st_im_text_unrealize;
|
||||
|
||||
actor_class->button_press_event = st_im_text_button_press_event;
|
||||
actor_class->key_press_event = st_im_text_key_press_event;
|
||||
actor_class->key_release_event = st_im_text_key_release_event;
|
||||
actor_class->key_focus_in = st_im_text_key_focus_in;
|
||||
actor_class->key_focus_out = st_im_text_key_focus_out;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_init (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv;
|
||||
|
||||
self->priv = priv = ST_IM_TEXT_GET_PRIVATE (self);
|
||||
|
||||
priv->im_context = gtk_im_multicontext_new ();
|
||||
g_signal_connect (priv->im_context, "commit",
|
||||
G_CALLBACK (st_im_text_commit_cb), self);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_im_text_new:
|
||||
* @text: text to set to
|
||||
*
|
||||
* Create a new #StIMText with the specified text
|
||||
*
|
||||
* Returns: a new #ClutterActor
|
||||
*/
|
||||
ClutterActor *
|
||||
st_im_text_new (const gchar *text)
|
||||
{
|
||||
return g_object_new (ST_TYPE_IM_TEXT,
|
||||
"text", text,
|
||||
NULL);
|
||||
}
|
69
src/st/st-im-text.h
Normal file
69
src/st/st-im-text.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-imtext.h
|
||||
*
|
||||
* This is a copy of ClutterIMText converted to use GtkIMContext rather
|
||||
* than ClutterIMContext. Original code:
|
||||
*
|
||||
* Author: raymond liu <raymond.liu@intel.com>
|
||||
*
|
||||
* Copyright (C) 2009, Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that 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 library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_IM_TEXT_H__
|
||||
#define __ST_IM_TEXT_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define ST_TYPE_IM_TEXT (st_im_text_get_type ())
|
||||
#define ST_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_IM_TEXT, StIMText))
|
||||
#define ST_IS_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_IM_TEXT))
|
||||
#define ST_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_IM_TEXT, StIMTextClass))
|
||||
#define ST_IS_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_IM_TEXT))
|
||||
#define ST_IM_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_IM_TEXT, StIMTextClass))
|
||||
|
||||
typedef struct _StIMText StIMText;
|
||||
typedef struct _StIMTextPrivate StIMTextPrivate;
|
||||
typedef struct _StIMTextClass StIMTextClass;
|
||||
|
||||
struct _StIMText
|
||||
{
|
||||
ClutterText parent_instance;
|
||||
|
||||
StIMTextPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StIMTextClass
|
||||
{
|
||||
ClutterTextClass parent_class;
|
||||
};
|
||||
|
||||
GType st_im_text_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor *st_im_text_new (const gchar *text);
|
||||
void st_im_text_set_autoshow_im (StIMText *self,
|
||||
gboolean autoshow);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_IM_TEXT_H__ */
|
342
src/st/st-label.c
Normal file
342
src/st/st-label.c
Normal file
@ -0,0 +1,342 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-label.c: Plain label actor
|
||||
*
|
||||
* Copyright 2008,2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-label
|
||||
* @short_description: Widget for displaying text
|
||||
*
|
||||
* #StLabel is a simple widget for displaying text. It derives from
|
||||
* #StWidget to add extra style and placement functionality over
|
||||
* #ClutterText. The internal #ClutterText is publicly accessibly to allow
|
||||
* applications to set further properties.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-label.h"
|
||||
|
||||
#include "st-widget.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_CLUTTER_TEXT,
|
||||
PROP_TEXT
|
||||
};
|
||||
|
||||
#define ST_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_LABEL, StLabelPrivate))
|
||||
|
||||
struct _StLabelPrivate
|
||||
{
|
||||
ClutterActor *label;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StLabel, st_label, ST_TYPE_WIDGET);
|
||||
|
||||
static void
|
||||
st_label_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StLabel *label = ST_LABEL (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
st_label_set_text (label, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CLUTTER_TEXT:
|
||||
g_value_set_object (value, priv->label);
|
||||
break;
|
||||
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_style_changed (StWidget *self)
|
||||
{
|
||||
StLabelPrivate *priv;
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string;
|
||||
|
||||
priv = ST_LABEL (self)->priv;
|
||||
theme_node = st_widget_get_theme_node (self);
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (priv->label), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
|
||||
g_free (font_string);
|
||||
|
||||
ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
clutter_actor_get_preferred_width (priv->label, for_height,
|
||||
min_width_p,
|
||||
natural_width_p);
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
clutter_actor_get_preferred_height (priv->label, for_width,
|
||||
min_height_p,
|
||||
natural_height_p);
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
ClutterActorClass *parent_class;
|
||||
ClutterActorBox content_box;
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &content_box);
|
||||
|
||||
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
|
||||
parent_class->allocate (actor, box, flags);
|
||||
|
||||
clutter_actor_allocate (priv->label, &content_box, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_paint (ClutterActor *actor)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
ClutterActorClass *parent_class;
|
||||
|
||||
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
|
||||
parent_class->paint (actor);
|
||||
|
||||
clutter_actor_paint (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_map (ClutterActor *actor)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_label_parent_class)->map (actor);
|
||||
|
||||
clutter_actor_map (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_unmap (ClutterActor *actor)
|
||||
{
|
||||
StLabelPrivate *priv = ST_LABEL (actor)->priv;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_label_parent_class)->unmap (actor);
|
||||
|
||||
clutter_actor_unmap (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_class_init (StLabelClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StLabelPrivate));
|
||||
|
||||
gobject_class->set_property = st_label_set_property;
|
||||
gobject_class->get_property = st_label_get_property;
|
||||
|
||||
actor_class->paint = st_label_paint;
|
||||
actor_class->allocate = st_label_allocate;
|
||||
actor_class->get_preferred_width = st_label_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_label_get_preferred_height;
|
||||
actor_class->map = st_label_map;
|
||||
actor_class->unmap = st_label_unmap;
|
||||
|
||||
widget_class->style_changed = st_label_style_changed;
|
||||
|
||||
pspec = g_param_spec_object ("clutter-text",
|
||||
"Clutter Text",
|
||||
"Internal ClutterText actor",
|
||||
CLUTTER_TYPE_TEXT,
|
||||
G_PARAM_READABLE);
|
||||
g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
|
||||
|
||||
pspec = g_param_spec_string ("text",
|
||||
"Text",
|
||||
"Text of the label",
|
||||
NULL, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
st_label_init (StLabel *label)
|
||||
{
|
||||
StLabelPrivate *priv;
|
||||
|
||||
label->priv = priv = ST_LABEL_GET_PRIVATE (label);
|
||||
|
||||
label->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
NULL);
|
||||
|
||||
clutter_actor_set_parent (priv->label, CLUTTER_ACTOR (label));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_label_new:
|
||||
* @text: text to set the label to
|
||||
*
|
||||
* Create a new #StLabel with the specified label
|
||||
*
|
||||
* Returns: a new #StLabel
|
||||
*/
|
||||
StWidget *
|
||||
st_label_new (const gchar *text)
|
||||
{
|
||||
if (text == NULL || *text == '\0')
|
||||
return g_object_new (ST_TYPE_LABEL, NULL);
|
||||
else
|
||||
return g_object_new (ST_TYPE_LABEL,
|
||||
"text", text,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_label_get_text:
|
||||
* @label: a #StLabel
|
||||
*
|
||||
* Get the text displayed on the label
|
||||
*
|
||||
* Returns: the text for the label. This must not be freed by the application
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
st_label_get_text (StLabel *label)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_LABEL (label), NULL);
|
||||
|
||||
return clutter_text_get_text (CLUTTER_TEXT (label->priv->label));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_label_set_text:
|
||||
* @label: a #StLabel
|
||||
* @text: text to set the label to
|
||||
*
|
||||
* Sets the text displayed on the label
|
||||
*/
|
||||
void
|
||||
st_label_set_text (StLabel *label,
|
||||
const gchar *text)
|
||||
{
|
||||
StLabelPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_LABEL (label));
|
||||
g_return_if_fail (text != NULL);
|
||||
|
||||
priv = label->priv;
|
||||
|
||||
clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
|
||||
|
||||
g_object_notify (G_OBJECT (label), "text");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_label_get_clutter_text:
|
||||
* @label: a #StLabel
|
||||
*
|
||||
* Retrieve the internal #ClutterText so that extra parameters can be set
|
||||
*
|
||||
* Returns: (transfer none): ethe #ClutterText used by #StLabel. The label
|
||||
* is owned by the #StLabel and should not be unref'ed by the application.
|
||||
*/
|
||||
ClutterActor*
|
||||
st_label_get_clutter_text (StLabel *label)
|
||||
{
|
||||
g_return_val_if_fail (ST_LABEL (label), NULL);
|
||||
|
||||
return label->priv->label;
|
||||
}
|
76
src/st/st-label.h
Normal file
76
src/st/st-label.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-label.h: Plain label actor
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_LABEL_H__
|
||||
#define __ST_LABEL_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <st/st-widget.h>
|
||||
|
||||
#define ST_TYPE_LABEL (st_label_get_type ())
|
||||
#define ST_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_LABEL, StLabel))
|
||||
#define ST_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_LABEL))
|
||||
#define ST_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_LABEL, StLabelClass))
|
||||
#define ST_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_LABEL))
|
||||
#define ST_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_LABEL, StLabelClass))
|
||||
|
||||
typedef struct _StLabel StLabel;
|
||||
typedef struct _StLabelPrivate StLabelPrivate;
|
||||
typedef struct _StLabelClass StLabelClass;
|
||||
|
||||
/**
|
||||
* StLabel:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
struct _StLabel
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent_instance;
|
||||
|
||||
StLabelPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StLabelClass
|
||||
{
|
||||
StWidgetClass parent_class;
|
||||
};
|
||||
|
||||
GType st_label_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget * st_label_new (const gchar *text);
|
||||
G_CONST_RETURN gchar *st_label_get_text (StLabel *label);
|
||||
void st_label_set_text (StLabel *label,
|
||||
const gchar *text);
|
||||
ClutterActor * st_label_get_clutter_text (StLabel *label);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_LABEL_H__ */
|
12
src/st/st-marshal.list
Normal file
12
src/st/st-marshal.list
Normal file
@ -0,0 +1,12 @@
|
||||
VOID:OBJECT
|
||||
VOID:VOID
|
||||
VOID:PARAM
|
||||
VOID:POINTER
|
||||
VOID:UINT
|
||||
VOID:UINT,UINT
|
||||
VOID:OBJECT,OBJECT
|
||||
VOID:STRING,OBJECT
|
||||
VOID:OBJECT,OBJECT,INT,INT
|
||||
VOID:OBJECT,FLOAT,FLOAT,INT,ENUM
|
||||
VOID:FLOAT,FLOAT,INT,ENUM
|
||||
VOID:FLOAT,FLOAT
|
112
src/st/st-private.c
Normal file
112
src/st/st-private.c
Normal file
@ -0,0 +1,112 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#include "st-private.h"
|
||||
|
||||
/* Utility function to modify a child allocation box with respect to the
|
||||
* x/y-fill child properties. Expects childbox to contain the available
|
||||
* allocation space.
|
||||
*/
|
||||
void
|
||||
_st_allocate_fill (ClutterActor *child,
|
||||
ClutterActorBox *childbox,
|
||||
StAlign x_alignment,
|
||||
StAlign y_alignment,
|
||||
gboolean x_fill,
|
||||
gboolean y_fill)
|
||||
{
|
||||
gfloat natural_width, natural_height;
|
||||
gfloat min_width, min_height;
|
||||
gfloat child_width, child_height;
|
||||
gfloat available_width, available_height;
|
||||
ClutterRequestMode request;
|
||||
ClutterActorBox allocation = { 0, };
|
||||
gdouble x_align, y_align;
|
||||
|
||||
if (x_alignment == ST_ALIGN_START)
|
||||
x_align = 0.0;
|
||||
else if (x_alignment == ST_ALIGN_MIDDLE)
|
||||
x_align = 0.5;
|
||||
else
|
||||
x_align = 1.0;
|
||||
|
||||
if (y_alignment == ST_ALIGN_START)
|
||||
y_align = 0.0;
|
||||
else if (y_alignment == ST_ALIGN_MIDDLE)
|
||||
y_align = 0.5;
|
||||
else
|
||||
y_align = 1.0;
|
||||
|
||||
available_width = childbox->x2 - childbox->x1;
|
||||
available_height = childbox->y2 - childbox->y1;
|
||||
|
||||
if (available_width < 0)
|
||||
available_width = 0;
|
||||
|
||||
if (available_height < 0)
|
||||
available_height = 0;
|
||||
|
||||
if (x_fill)
|
||||
{
|
||||
allocation.x1 = childbox->x1;
|
||||
allocation.x2 = (int)(allocation.x1 + available_width);
|
||||
}
|
||||
|
||||
if (y_fill)
|
||||
{
|
||||
allocation.y1 = childbox->y1;
|
||||
allocation.y2 = (int)(allocation.y1 + available_height);
|
||||
}
|
||||
|
||||
/* if we are filling horizontally and vertically then we're done */
|
||||
if (x_fill && y_fill)
|
||||
{
|
||||
*childbox = allocation;
|
||||
return;
|
||||
}
|
||||
|
||||
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
|
||||
|
||||
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
|
||||
{
|
||||
clutter_actor_get_preferred_width (child, available_height,
|
||||
&min_width,
|
||||
&natural_width);
|
||||
|
||||
child_width = CLAMP (natural_width, min_width, available_width);
|
||||
|
||||
clutter_actor_get_preferred_height (child, child_width,
|
||||
&min_height,
|
||||
&natural_height);
|
||||
|
||||
child_height = CLAMP (natural_height, min_height, available_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_actor_get_preferred_height (child, available_width,
|
||||
&min_height,
|
||||
&natural_height);
|
||||
|
||||
child_height = CLAMP (natural_height, min_height, available_height);
|
||||
|
||||
clutter_actor_get_preferred_width (child, child_height,
|
||||
&min_width,
|
||||
&natural_width);
|
||||
|
||||
child_width = CLAMP (natural_width, min_width, available_width);
|
||||
}
|
||||
|
||||
if (!x_fill)
|
||||
{
|
||||
allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align);
|
||||
allocation.x2 = allocation.x1 + (int) child_width;
|
||||
}
|
||||
|
||||
if (!y_fill)
|
||||
{
|
||||
allocation.y1 = childbox->y1 + (int)((available_height - child_height) * y_align);
|
||||
allocation.y2 = allocation.y1 + (int) child_height;
|
||||
}
|
||||
|
||||
*childbox = allocation;
|
||||
|
||||
}
|
58
src/st/st-private.h
Normal file
58
src/st/st-private.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-private.h: Private declarations
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ST_PRIVATE_H__
|
||||
#define __ST_PRIVATE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "st-widget.h"
|
||||
#include "st-bin.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define I_(str) (g_intern_static_string ((str)))
|
||||
|
||||
#define ST_PARAM_READABLE \
|
||||
(G_PARAM_READABLE | \
|
||||
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
|
||||
|
||||
#define ST_PARAM_READWRITE \
|
||||
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
|
||||
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
|
||||
|
||||
void _st_bin_get_align_factors (StBin *bin,
|
||||
gdouble *x_align,
|
||||
gdouble *y_align);
|
||||
|
||||
void _st_allocate_fill (ClutterActor *child,
|
||||
ClutterActorBox *childbox,
|
||||
StAlign x_align,
|
||||
StAlign y_align,
|
||||
gboolean x_fill,
|
||||
gboolean y_fill);
|
||||
|
||||
#endif /* __ST_PRIVATE_H__ */
|
1077
src/st/st-scroll-bar.c
Normal file
1077
src/st/st-scroll-bar.c
Normal file
File diff suppressed because it is too large
Load Diff
83
src/st/st-scroll-bar.h
Normal file
83
src/st/st-scroll-bar.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-scroll-bar.h: Scroll bar actor
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_SCROLL_BAR_H__
|
||||
#define __ST_SCROLL_BAR_H__
|
||||
|
||||
#include <st/st-adjustment.h>
|
||||
#include <st/st-bin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_SCROLL_BAR (st_scroll_bar_get_type())
|
||||
#define ST_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_BAR, StScrollBar))
|
||||
#define ST_IS_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_BAR))
|
||||
#define ST_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_BAR, StScrollBarClass))
|
||||
#define ST_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_BAR))
|
||||
#define ST_SCROLL_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_BAR, StScrollBarClass))
|
||||
|
||||
typedef struct _StScrollBar StScrollBar;
|
||||
typedef struct _StScrollBarPrivate StScrollBarPrivate;
|
||||
typedef struct _StScrollBarClass StScrollBarClass;
|
||||
|
||||
/**
|
||||
* StScrollBar:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StScrollBar
|
||||
{
|
||||
/*< private >*/
|
||||
StBin parent_instance;
|
||||
|
||||
StScrollBarPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StScrollBarClass
|
||||
{
|
||||
StBinClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*scroll_start) (StScrollBar *bar);
|
||||
void (*scroll_stop) (StScrollBar *bar);
|
||||
};
|
||||
|
||||
GType st_scroll_bar_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget *st_scroll_bar_new (StAdjustment *adjustment);
|
||||
|
||||
void st_scroll_bar_set_adjustment (StScrollBar *bar,
|
||||
StAdjustment *adjustment);
|
||||
StAdjustment *st_scroll_bar_get_adjustment (StScrollBar *bar);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_SCROLL_BAR_H__ */
|
844
src/st/st-scroll-view.c
Normal file
844
src/st/st-scroll-view.c
Normal file
@ -0,0 +1,844 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-scroll-view.h: Container with scroll-bars
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-scroll-view
|
||||
* @short_description: a container for scrollable children
|
||||
*
|
||||
* #StScrollView is a single child container for actors that implement
|
||||
* #StScrollable. It provides scrollbars around the edge of the child to
|
||||
* allow the user to move around the scrollable area.
|
||||
*/
|
||||
|
||||
#include "st-scroll-view.h"
|
||||
#include "st-marshal.h"
|
||||
#include "st-scroll-bar.h"
|
||||
#include "st-scrollable.h"
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
static void clutter_container_iface_init (ClutterContainerIface *iface);
|
||||
|
||||
static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
|
||||
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
|
||||
clutter_container_iface_init))
|
||||
|
||||
#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
|
||||
ST_TYPE_SCROLL_VIEW, \
|
||||
StScrollViewPrivate))
|
||||
|
||||
/* Default width (or height - the narrow dimension) for the scrollbars*/
|
||||
#define DEFAULT_SCROLLBAR_WIDTH 24
|
||||
|
||||
struct _StScrollViewPrivate
|
||||
{
|
||||
/* a pointer to the child; this is actually stored
|
||||
* inside StBin:child, but we keep it to avoid
|
||||
* calling st_bin_get_child() every time we need it
|
||||
*/
|
||||
ClutterActor *child;
|
||||
|
||||
ClutterActor *hscroll;
|
||||
ClutterActor *vscroll;
|
||||
|
||||
gfloat row_size;
|
||||
gfloat column_size;
|
||||
|
||||
gboolean row_size_set : 1;
|
||||
gboolean column_size_set : 1;
|
||||
guint mouse_scroll : 1;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_HSCROLL,
|
||||
PROP_VSCROLL,
|
||||
PROP_MOUSE_SCROLL
|
||||
};
|
||||
|
||||
static void
|
||||
st_scroll_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HSCROLL:
|
||||
g_value_set_object (value, priv->hscroll);
|
||||
break;
|
||||
case PROP_VSCROLL:
|
||||
g_value_set_object (value, priv->vscroll);
|
||||
break;
|
||||
case PROP_MOUSE_SCROLL:
|
||||
g_value_set_boolean (value, priv->mouse_scroll);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MOUSE_SCROLL:
|
||||
st_scroll_view_set_mouse_scrolling ((StScrollView *) object,
|
||||
g_value_get_boolean (value));
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_dispose (GObject *object)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
|
||||
|
||||
priv->child = NULL;
|
||||
|
||||
if (priv->vscroll)
|
||||
{
|
||||
clutter_actor_unparent (priv->vscroll);
|
||||
priv->vscroll = NULL;
|
||||
}
|
||||
|
||||
if (priv->hscroll)
|
||||
{
|
||||
clutter_actor_unparent (priv->hscroll);
|
||||
priv->hscroll = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_finalize (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (st_scroll_view_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_paint (ClutterActor *actor)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
|
||||
|
||||
/* StBin will paint the child */
|
||||
CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->paint (actor);
|
||||
|
||||
/* paint our custom children */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
|
||||
clutter_actor_paint (priv->hscroll);
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
|
||||
clutter_actor_paint (priv->vscroll);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_pick (ClutterActor *actor,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
|
||||
|
||||
/* Chain up so we get a bounding box pained (if we are reactive) */
|
||||
CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, color);
|
||||
|
||||
/* paint our custom children */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
|
||||
clutter_actor_paint (priv->hscroll);
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
|
||||
clutter_actor_paint (priv->vscroll);
|
||||
}
|
||||
|
||||
static double
|
||||
get_scrollbar_width (StScrollView *scroll_view)
|
||||
{
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (scroll_view));
|
||||
double result = DEFAULT_SCROLLBAR_WIDTH;
|
||||
|
||||
st_theme_node_get_length (theme_node, "scrollbar-width", FALSE, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static double
|
||||
get_scrollbar_height (StScrollView *scroll_view)
|
||||
{
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (scroll_view));
|
||||
double result = DEFAULT_SCROLLBAR_WIDTH;
|
||||
|
||||
st_theme_node_get_length (theme_node, "scrollbar-height", FALSE, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
if (!priv->child)
|
||||
return;
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
/* Our natural width is the natural width of the child */
|
||||
clutter_actor_get_preferred_width (priv->child,
|
||||
for_height,
|
||||
NULL,
|
||||
natural_width_p);
|
||||
|
||||
/* Add space for the scroll-bar if we can determine it will be necessary */
|
||||
if ((for_height >= 0) && natural_width_p)
|
||||
{
|
||||
gfloat natural_height;
|
||||
|
||||
clutter_actor_get_preferred_height (priv->child, -1.0,
|
||||
NULL,
|
||||
&natural_height);
|
||||
if (for_height < natural_height)
|
||||
*natural_width_p += get_scrollbar_width (ST_SCROLL_VIEW (actor));
|
||||
}
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p = 0;
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
if (!priv->child)
|
||||
return;
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
/* Our natural height is the natural height of the child */
|
||||
clutter_actor_get_preferred_height (priv->child,
|
||||
for_width,
|
||||
NULL,
|
||||
natural_height_p);
|
||||
|
||||
/* Add space for the scroll-bar if we can determine it will be necessary */
|
||||
if ((for_width >= 0) && natural_height_p)
|
||||
{
|
||||
gfloat natural_width;
|
||||
|
||||
clutter_actor_get_preferred_width (priv->child, -1.0,
|
||||
NULL,
|
||||
&natural_width);
|
||||
if (for_width < natural_width)
|
||||
*natural_height_p += get_scrollbar_height (ST_SCROLL_VIEW (actor));
|
||||
}
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = 0;
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
ClutterActorBox content_box, child_box;
|
||||
ClutterActorClass *parent_parent_class;
|
||||
gfloat avail_width, avail_height, sb_width, sb_height;
|
||||
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
/* Chain up to the parent's parent class
|
||||
*
|
||||
* We do this because we do not want StBin to allocate the child, as we
|
||||
* give it a different allocation later, depending on whether the scrollbars
|
||||
* are visible
|
||||
*/
|
||||
parent_parent_class
|
||||
= g_type_class_peek_parent (st_scroll_view_parent_class);
|
||||
|
||||
CLUTTER_ACTOR_CLASS (parent_parent_class)->
|
||||
allocate (actor, box, flags);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor));
|
||||
sb_height = get_scrollbar_width (ST_SCROLL_VIEW (actor));
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
|
||||
sb_width = 0;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
|
||||
sb_height = 0;
|
||||
|
||||
/* Vertical scrollbar */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
|
||||
{
|
||||
child_box.x1 = content_box.x2 - sb_width;
|
||||
child_box.y1 = content_box.y1;
|
||||
child_box.x2 = content_box.x2;
|
||||
child_box.y2 = content_box.y2 - sb_height;
|
||||
|
||||
clutter_actor_allocate (priv->vscroll, &child_box, flags);
|
||||
}
|
||||
|
||||
/* Horizontal scrollbar */
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
|
||||
{
|
||||
child_box.x1 = content_box.x1;
|
||||
child_box.y1 = content_box.y2 - sb_height;
|
||||
child_box.x2 = content_box.x2 - sb_width;
|
||||
child_box.y2 = content_box.y2;
|
||||
|
||||
clutter_actor_allocate (priv->hscroll, &child_box, flags);
|
||||
}
|
||||
|
||||
|
||||
/* Child */
|
||||
child_box.x1 = content_box.x1;
|
||||
child_box.y1 = content_box.y1;
|
||||
child_box.x2 = content_box.x2 - sb_width;
|
||||
child_box.y2 = content_box.y2 - sb_height;
|
||||
|
||||
if (priv->child)
|
||||
clutter_actor_allocate (priv->child, &child_box, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_style_changed (StWidget *widget)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (widget)->priv;
|
||||
|
||||
st_widget_style_changed (ST_WIDGET (priv->hscroll));
|
||||
st_widget_style_changed (ST_WIDGET (priv->vscroll));
|
||||
|
||||
ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_scroll_view_scroll_event (ClutterActor *self,
|
||||
ClutterScrollEvent *event)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
|
||||
gdouble lower, value, upper, step;
|
||||
StAdjustment *vadjustment, *hadjustment;
|
||||
|
||||
/* don't handle scroll events if requested not to */
|
||||
if (!priv->mouse_scroll)
|
||||
return FALSE;
|
||||
|
||||
hadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
|
||||
vadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
if (vadjustment)
|
||||
g_object_get (vadjustment,
|
||||
"lower", &lower,
|
||||
"step-increment", &step,
|
||||
"value", &value,
|
||||
"upper", &upper,
|
||||
NULL);
|
||||
else
|
||||
return FALSE;
|
||||
break;
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
if (vadjustment)
|
||||
g_object_get (hadjustment,
|
||||
"lower", &lower,
|
||||
"step-increment", &step,
|
||||
"value", &value,
|
||||
"upper", &upper,
|
||||
NULL);
|
||||
else
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
if (value == lower)
|
||||
return FALSE;
|
||||
else
|
||||
st_adjustment_set_value (vadjustment, value - step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
if (value == upper)
|
||||
return FALSE;
|
||||
else
|
||||
st_adjustment_set_value (vadjustment, value + step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
if (value == lower)
|
||||
return FALSE;
|
||||
else
|
||||
st_adjustment_set_value (hadjustment, value - step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
if (value == upper)
|
||||
return FALSE;
|
||||
else
|
||||
st_adjustment_set_value (hadjustment, value + step);
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_class_init (StScrollViewClass *klass)
|
||||
{
|
||||
GParamSpec *pspec;
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StScrollViewPrivate));
|
||||
|
||||
object_class->get_property = st_scroll_view_get_property;
|
||||
object_class->set_property = st_scroll_view_set_property;
|
||||
object_class->dispose= st_scroll_view_dispose;
|
||||
object_class->finalize = st_scroll_view_finalize;
|
||||
|
||||
actor_class->paint = st_scroll_view_paint;
|
||||
actor_class->pick = st_scroll_view_pick;
|
||||
actor_class->get_preferred_width = st_scroll_view_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_scroll_view_get_preferred_height;
|
||||
actor_class->allocate = st_scroll_view_allocate;
|
||||
actor_class->scroll_event = st_scroll_view_scroll_event;
|
||||
|
||||
widget_class->style_changed = st_scroll_view_style_changed;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_HSCROLL,
|
||||
g_param_spec_object ("hscroll",
|
||||
"StScrollBar",
|
||||
"Horizontal scroll indicator",
|
||||
ST_TYPE_SCROLL_BAR,
|
||||
G_PARAM_READABLE));
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_VSCROLL,
|
||||
g_param_spec_object ("vscroll",
|
||||
"StScrollBar",
|
||||
"Vertical scroll indicator",
|
||||
ST_TYPE_SCROLL_BAR,
|
||||
G_PARAM_READABLE));
|
||||
|
||||
pspec = g_param_spec_boolean ("enable-mouse-scrolling",
|
||||
"Enable Mouse Scrolling",
|
||||
"Enable automatic mouse wheel scrolling",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MOUSE_SCROLL,
|
||||
pspec);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
child_adjustment_changed_cb (StAdjustment *adjustment,
|
||||
ClutterActor *bar)
|
||||
{
|
||||
StScrollView *scroll;
|
||||
gdouble lower, upper, page_size;
|
||||
|
||||
scroll = ST_SCROLL_VIEW (clutter_actor_get_parent (bar));
|
||||
|
||||
/* Determine if this scroll-bar should be visible */
|
||||
st_adjustment_get_values (adjustment, NULL,
|
||||
&lower, &upper,
|
||||
NULL, NULL,
|
||||
&page_size);
|
||||
|
||||
if ((upper - lower) > page_size)
|
||||
clutter_actor_show (bar);
|
||||
else
|
||||
clutter_actor_hide (bar);
|
||||
|
||||
/* Request a resize */
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
|
||||
}
|
||||
|
||||
static void
|
||||
child_hadjustment_notify_cb (GObject *gobject,
|
||||
GParamSpec *arg1,
|
||||
gpointer user_data)
|
||||
{
|
||||
StAdjustment *hadjust;
|
||||
|
||||
ClutterActor *actor = CLUTTER_ACTOR (gobject);
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
|
||||
|
||||
hadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
|
||||
if (hadjust)
|
||||
g_signal_handlers_disconnect_by_func (hadjust,
|
||||
child_adjustment_changed_cb,
|
||||
priv->hscroll);
|
||||
|
||||
st_scrollable_get_adjustments (ST_SCROLLABLE (actor), &hadjust, NULL);
|
||||
if (hadjust)
|
||||
{
|
||||
/* Force scroll step if neede. */
|
||||
if (priv->column_size_set)
|
||||
{
|
||||
g_object_set (hadjust,
|
||||
"step-increment", priv->column_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->hscroll), hadjust);
|
||||
g_signal_connect (hadjust, "changed", G_CALLBACK (
|
||||
child_adjustment_changed_cb), priv->hscroll);
|
||||
child_adjustment_changed_cb (hadjust, priv->hscroll);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
child_vadjustment_notify_cb (GObject *gobject,
|
||||
GParamSpec *arg1,
|
||||
gpointer user_data)
|
||||
{
|
||||
StAdjustment *vadjust;
|
||||
|
||||
ClutterActor *actor = CLUTTER_ACTOR (gobject);
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
|
||||
|
||||
vadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
|
||||
if (vadjust)
|
||||
g_signal_handlers_disconnect_by_func (vadjust,
|
||||
child_adjustment_changed_cb,
|
||||
priv->vscroll);
|
||||
|
||||
st_scrollable_get_adjustments (ST_SCROLLABLE(actor), NULL, &vadjust);
|
||||
if (vadjust)
|
||||
{
|
||||
/* Force scroll step if neede. */
|
||||
if (priv->row_size_set)
|
||||
{
|
||||
g_object_set (vadjust,
|
||||
"step-increment", priv->row_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->vscroll), vadjust);
|
||||
g_signal_connect (vadjust, "changed", G_CALLBACK (
|
||||
child_adjustment_changed_cb), priv->vscroll);
|
||||
child_adjustment_changed_cb (vadjust, priv->vscroll);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_init (StScrollView *self)
|
||||
{
|
||||
StScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
|
||||
|
||||
priv->hscroll = CLUTTER_ACTOR (st_scroll_bar_new (NULL));
|
||||
priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR, "vertical", TRUE, NULL);
|
||||
|
||||
clutter_actor_set_parent (priv->hscroll, CLUTTER_ACTOR (self));
|
||||
clutter_actor_set_parent (priv->vscroll, CLUTTER_ACTOR (self));
|
||||
|
||||
/* mouse scroll is enabled by default, so we also need to be reactive */
|
||||
priv->mouse_scroll = TRUE;
|
||||
g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_add (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StScrollView *self = ST_SCROLL_VIEW (container);
|
||||
StScrollViewPrivate *priv = self->priv;
|
||||
|
||||
if (ST_IS_SCROLLABLE (actor))
|
||||
{
|
||||
priv->child = actor;
|
||||
|
||||
/* chain up to StBin::add() */
|
||||
st_scroll_view_parent_iface->add (container, actor);
|
||||
|
||||
/* Get adjustments for scroll-bars */
|
||||
g_signal_connect (actor, "notify::hadjustment",
|
||||
G_CALLBACK (child_hadjustment_notify_cb),
|
||||
container);
|
||||
g_signal_connect (actor, "notify::vadjustment",
|
||||
G_CALLBACK (child_vadjustment_notify_cb),
|
||||
container);
|
||||
child_hadjustment_notify_cb (G_OBJECT (actor), NULL, container);
|
||||
child_vadjustment_notify_cb (G_OBJECT (actor), NULL, container);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Attempting to add an actor of type %s to "
|
||||
"a StScrollView, but the actor does "
|
||||
"not implement StScrollable.",
|
||||
g_type_name (G_OBJECT_TYPE (actor)));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_remove (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
|
||||
|
||||
if (actor == priv->child)
|
||||
{
|
||||
g_object_ref (priv->child);
|
||||
|
||||
/* chain up to StBin::remove() */
|
||||
st_scroll_view_parent_iface->remove (container, actor);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (priv->child,
|
||||
child_hadjustment_notify_cb,
|
||||
container);
|
||||
g_signal_handlers_disconnect_by_func (priv->child,
|
||||
child_vadjustment_notify_cb,
|
||||
container);
|
||||
st_scrollable_set_adjustments ((StScrollable*) priv->child, NULL, NULL);
|
||||
|
||||
g_object_unref (priv->child);
|
||||
priv->child = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_foreach_with_internals (ClutterContainer *container,
|
||||
ClutterCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
|
||||
|
||||
if (priv->child != NULL)
|
||||
callback (priv->child, user_data);
|
||||
|
||||
if (priv->hscroll != NULL)
|
||||
callback (priv->hscroll, user_data);
|
||||
|
||||
if (priv->vscroll != NULL)
|
||||
callback (priv->vscroll, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_container_iface_init (ClutterContainerIface *iface)
|
||||
{
|
||||
/* store a pointer to the StBin implementation of
|
||||
* ClutterContainer so that we can chain up when
|
||||
* overriding the methods
|
||||
*/
|
||||
st_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
|
||||
|
||||
iface->add = st_scroll_view_add;
|
||||
iface->remove = st_scroll_view_remove;
|
||||
iface->foreach_with_internals = st_scroll_view_foreach_with_internals;
|
||||
}
|
||||
|
||||
StWidget *
|
||||
st_scroll_view_new (void)
|
||||
{
|
||||
return g_object_new (ST_TYPE_SCROLL_VIEW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_scroll_view_get_hscroll_bar:
|
||||
* @scroll: a #StScrollView
|
||||
*
|
||||
* Gets the horizontal scrollbar of the scrollbiew
|
||||
*
|
||||
* Return value: (transfer none): the horizontal #StScrollbar
|
||||
*/
|
||||
ClutterActor *
|
||||
st_scroll_view_get_hscroll_bar (StScrollView *scroll)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
|
||||
|
||||
return scroll->priv->hscroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_scroll_view_get_vscroll_bar:
|
||||
* @scroll: a #StScrollView
|
||||
*
|
||||
* Gets the vertical scrollbar of the scrollbiew
|
||||
*
|
||||
* Return value: (transfer none): the vertical #StScrollbar
|
||||
*/
|
||||
ClutterActor *
|
||||
st_scroll_view_get_vscroll_bar (StScrollView *scroll)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
|
||||
|
||||
return scroll->priv->vscroll;
|
||||
}
|
||||
|
||||
gfloat
|
||||
st_scroll_view_get_column_size (StScrollView *scroll)
|
||||
{
|
||||
StAdjustment *adjustment;
|
||||
gdouble column_size;
|
||||
|
||||
g_return_val_if_fail (scroll, 0);
|
||||
|
||||
adjustment = st_scroll_bar_get_adjustment (
|
||||
ST_SCROLL_BAR (scroll->priv->hscroll));
|
||||
g_object_get (adjustment,
|
||||
"step-increment", &column_size,
|
||||
NULL);
|
||||
|
||||
return column_size;
|
||||
}
|
||||
|
||||
void
|
||||
st_scroll_view_set_column_size (StScrollView *scroll,
|
||||
gfloat column_size)
|
||||
{
|
||||
StAdjustment *adjustment;
|
||||
|
||||
g_return_if_fail (scroll);
|
||||
|
||||
if (column_size < 0)
|
||||
{
|
||||
scroll->priv->column_size_set = FALSE;
|
||||
scroll->priv->column_size = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
scroll->priv->column_size_set = TRUE;
|
||||
scroll->priv->column_size = column_size;
|
||||
|
||||
adjustment = st_scroll_bar_get_adjustment (
|
||||
ST_SCROLL_BAR (scroll->priv->hscroll));
|
||||
|
||||
if (adjustment)
|
||||
g_object_set (adjustment,
|
||||
"step-increment", (gdouble) scroll->priv->column_size,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
gfloat
|
||||
st_scroll_view_get_row_size (StScrollView *scroll)
|
||||
{
|
||||
StAdjustment *adjustment;
|
||||
gdouble row_size;
|
||||
|
||||
g_return_val_if_fail (scroll, 0);
|
||||
|
||||
adjustment = st_scroll_bar_get_adjustment (
|
||||
ST_SCROLL_BAR (scroll->priv->vscroll));
|
||||
g_object_get (adjustment,
|
||||
"step-increment", &row_size,
|
||||
NULL);
|
||||
|
||||
return row_size;
|
||||
}
|
||||
|
||||
void
|
||||
st_scroll_view_set_row_size (StScrollView *scroll,
|
||||
gfloat row_size)
|
||||
{
|
||||
StAdjustment *adjustment;
|
||||
|
||||
g_return_if_fail (scroll);
|
||||
|
||||
if (row_size < 0)
|
||||
{
|
||||
scroll->priv->row_size_set = FALSE;
|
||||
scroll->priv->row_size = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
scroll->priv->row_size_set = TRUE;
|
||||
scroll->priv->row_size = row_size;
|
||||
|
||||
adjustment = st_scroll_bar_get_adjustment (
|
||||
ST_SCROLL_BAR (scroll->priv->vscroll));
|
||||
|
||||
if (adjustment)
|
||||
g_object_set (adjustment,
|
||||
"step-increment", (gdouble) scroll->priv->row_size,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
|
||||
gboolean enabled)
|
||||
{
|
||||
StScrollViewPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
|
||||
|
||||
priv = ST_SCROLL_VIEW (scroll)->priv;
|
||||
|
||||
if (priv->mouse_scroll != enabled)
|
||||
{
|
||||
priv->mouse_scroll = enabled;
|
||||
|
||||
/* make sure we can receive mouse wheel events */
|
||||
if (enabled)
|
||||
clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
st_scroll_view_get_mouse_scrolling (StScrollView *scroll)
|
||||
{
|
||||
StScrollViewPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
|
||||
|
||||
priv = ST_SCROLL_VIEW (scroll)->priv;
|
||||
|
||||
return priv->mouse_scroll;
|
||||
}
|
89
src/st/st-scroll-view.h
Normal file
89
src/st/st-scroll-view.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-scroll-view.h: Container with scroll-bars
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_SCROLL_VIEW_H__
|
||||
#define __ST_SCROLL_VIEW_H__
|
||||
|
||||
#include <st/st-bin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_SCROLL_VIEW (st_scroll_view_get_type())
|
||||
#define ST_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW, StScrollView))
|
||||
#define ST_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW))
|
||||
#define ST_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
|
||||
#define ST_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW))
|
||||
#define ST_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
|
||||
|
||||
typedef struct _StScrollView StScrollView;
|
||||
typedef struct _StScrollViewPrivate StScrollViewPrivate;
|
||||
typedef struct _StScrollViewClass StScrollViewClass;
|
||||
|
||||
/**
|
||||
* StScrollView:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StScrollView
|
||||
{
|
||||
/*< private >*/
|
||||
StBin parent_instance;
|
||||
|
||||
StScrollViewPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StScrollViewClass
|
||||
{
|
||||
StBinClass parent_class;
|
||||
};
|
||||
|
||||
GType st_scroll_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget *st_scroll_view_new (void);
|
||||
|
||||
ClutterActor *st_scroll_view_get_hscroll_bar (StScrollView *scroll);
|
||||
ClutterActor *st_scroll_view_get_vscroll_bar (StScrollView *scroll);
|
||||
|
||||
gfloat st_scroll_view_get_column_size (StScrollView *scroll);
|
||||
void st_scroll_view_set_column_size (StScrollView *scroll,
|
||||
gfloat column_size);
|
||||
|
||||
gfloat st_scroll_view_get_row_size (StScrollView *scroll);
|
||||
void st_scroll_view_set_row_size (StScrollView *scroll,
|
||||
gfloat row_size);
|
||||
|
||||
void st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
|
||||
gboolean enabled);
|
||||
gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_SCROLL_VIEW_H__ */
|
97
src/st/st-scrollable.c
Normal file
97
src/st/st-scrollable.c
Normal file
@ -0,0 +1,97 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-scrollable.c: Scrollable interface
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "st-scrollable.h"
|
||||
|
||||
static void
|
||||
st_scrollable_base_init (gpointer g_iface)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
g_object_interface_install_property (g_iface,
|
||||
g_param_spec_object ("hadjustment",
|
||||
"StAdjustment",
|
||||
"Horizontal adjustment",
|
||||
ST_TYPE_ADJUSTMENT,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_interface_install_property (g_iface,
|
||||
g_param_spec_object ("vadjustment",
|
||||
"StAdjustment",
|
||||
"Vertical adjustment",
|
||||
ST_TYPE_ADJUSTMENT,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
initialized = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
GType
|
||||
st_scrollable_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0)
|
||||
{
|
||||
static const GTypeInfo info =
|
||||
{
|
||||
sizeof (StScrollableInterface),
|
||||
st_scrollable_base_init, /* base_init */
|
||||
NULL,
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_INTERFACE,
|
||||
"StScrollable", &info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void
|
||||
st_scrollable_set_adjustments (StScrollable *scrollable,
|
||||
StAdjustment *hadjustment,
|
||||
StAdjustment *vadjustment)
|
||||
{
|
||||
ST_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
|
||||
hadjustment,
|
||||
vadjustment);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_scroll_bar_get_adjustments:
|
||||
* @hadjustment: (transfer none) (out) (allow-none): location to store the horizontal adjustment, or %NULL
|
||||
* @vadjustment: (transfer none) (out) (allow-none): location to store the vertical adjustment, or %NULL
|
||||
*
|
||||
* Gets the adjustment objects that store the offsets of the scrollable widget
|
||||
* into its possible scrolling area.
|
||||
*/
|
||||
void
|
||||
st_scrollable_get_adjustments (StScrollable *scrollable,
|
||||
StAdjustment **hadjustment,
|
||||
StAdjustment **vadjustment)
|
||||
{
|
||||
ST_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
|
||||
hadjustment,
|
||||
vadjustment);
|
||||
}
|
70
src/st/st-scrollable.h
Normal file
70
src/st/st-scrollable.h
Normal file
@ -0,0 +1,70 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-scrollable.h: Scrollable interface
|
||||
*
|
||||
* Copyright 2008 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Chris Lord <chris@openedhand.com>
|
||||
* Port to St by: Robert Staudinger <robsta@openedhand.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_SCROLLABLE_H__
|
||||
#define __ST_SCROLLABLE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <st/st-adjustment.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_SCROLLABLE (st_scrollable_get_type ())
|
||||
#define ST_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLLABLE, StScrollable))
|
||||
#define ST_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLLABLE))
|
||||
#define ST_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), ST_TYPE_SCROLLABLE, StScrollableInterface))
|
||||
|
||||
typedef struct _StScrollable StScrollable; /* Dummy object */
|
||||
typedef struct _StScrollableInterface StScrollableInterface;
|
||||
|
||||
struct _StScrollableInterface
|
||||
{
|
||||
GTypeInterface parent;
|
||||
|
||||
void (* set_adjustments) (StScrollable *scrollable,
|
||||
StAdjustment *hadjustment,
|
||||
StAdjustment *vadjustment);
|
||||
void (* get_adjustments) (StScrollable *scrollable,
|
||||
StAdjustment **hadjustment,
|
||||
StAdjustment **vadjustment);
|
||||
};
|
||||
|
||||
GType st_scrollable_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void st_scrollable_set_adjustments (StScrollable *scrollable,
|
||||
StAdjustment *hadjustment,
|
||||
StAdjustment *vadjustment);
|
||||
void st_scrollable_get_adjustments (StScrollable *scrollable,
|
||||
StAdjustment **hadjustment,
|
||||
StAdjustment **vadjustment);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_SCROLLABLE_H__ */
|
576
src/st/st-subtexture.c
Normal file
576
src/st/st-subtexture.c
Normal file
@ -0,0 +1,576 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-subtexture.h: Class to wrap a texture and "subframe" it.
|
||||
* based on
|
||||
* st-texture-frame.c: Expandible texture actor
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cogl/cogl.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-subtexture.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_PARENT_TEXTURE,
|
||||
|
||||
PROP_TOP,
|
||||
PROP_LEFT,
|
||||
PROP_WIDTH,
|
||||
PROP_HEIGHT
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StSubtexture, st_subtexture, CLUTTER_TYPE_ACTOR);
|
||||
|
||||
#define ST_SUBTEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_SUBTEXTURE, StSubtexturePrivate))
|
||||
|
||||
struct _StSubtexturePrivate
|
||||
{
|
||||
ClutterTexture *parent_texture;
|
||||
|
||||
int left;
|
||||
int top;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
CoglHandle material;
|
||||
};
|
||||
|
||||
static void
|
||||
st_subtexture_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
|
||||
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
{
|
||||
if (min_width_p)
|
||||
*min_width_p = 0;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (min_width_p)
|
||||
*min_width_p = priv->width;
|
||||
if (natural_width_p)
|
||||
*natural_width_p = priv->width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_get_preferred_height (ClutterActor *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
|
||||
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
{
|
||||
if (min_height_p)
|
||||
*min_height_p = 0;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (min_height_p)
|
||||
*min_height_p = priv->height;
|
||||
if (natural_height_p)
|
||||
*natural_height_p = priv->height;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_realize (ClutterActor *self)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
|
||||
|
||||
if (priv->material != COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
priv->material = cogl_material_new ();
|
||||
|
||||
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_unrealize (ClutterActor *self)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
|
||||
|
||||
if (priv->material == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
cogl_material_unref (priv->material);
|
||||
priv->material = COGL_INVALID_HANDLE;
|
||||
|
||||
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_paint (ClutterActor *self)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
|
||||
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
|
||||
ClutterActorBox box = { 0, 0, 0, 0 };
|
||||
gfloat tx1, ty1, tx2, ty2, tex_width, tex_height, width, height;
|
||||
guint8 opacity;
|
||||
|
||||
/* no need to paint stuff if we don't have a texture */
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
return;
|
||||
|
||||
/* parent texture may have been hidden, so need to make sure it gets
|
||||
* realized
|
||||
*/
|
||||
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
|
||||
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
|
||||
|
||||
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
|
||||
if (cogl_texture == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
tex_width = cogl_texture_get_width (cogl_texture);
|
||||
tex_height = cogl_texture_get_height (cogl_texture);
|
||||
|
||||
clutter_actor_get_allocation_box (self, &box);
|
||||
|
||||
width = box.x2 - box.x1;
|
||||
height = box.y2 - box.y1;
|
||||
|
||||
tx1 = 1.0 * priv->left / tex_width;
|
||||
ty1 = 1.0 * priv->top / tex_height;
|
||||
|
||||
tx2 = 1.0 * (priv->left + priv->width) / tex_width;
|
||||
ty2 = 1.0 * (priv->top + priv->height) / tex_height;
|
||||
|
||||
|
||||
opacity = clutter_actor_get_paint_opacity (self);
|
||||
|
||||
g_assert (priv->material != COGL_INVALID_HANDLE);
|
||||
|
||||
/* set the source material using the parent texture's COGL handle */
|
||||
cogl_material_set_color4ub (priv->material, 255, 255, 255, opacity);
|
||||
cogl_material_set_layer (priv->material, 0, cogl_texture);
|
||||
cogl_set_source (priv->material);
|
||||
|
||||
cogl_rectangle_with_texture_coords (0,0, (float) width, (float) height,
|
||||
tx1, ty1, tx2, ty2);
|
||||
}
|
||||
|
||||
static inline void
|
||||
st_subtexture_set_frame_internal (StSubtexture *frame,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
StSubtexturePrivate *priv = frame->priv;
|
||||
GObject *gobject = G_OBJECT (frame);
|
||||
gboolean changed = FALSE;
|
||||
|
||||
g_object_freeze_notify (gobject);
|
||||
|
||||
if (priv->top != top)
|
||||
{
|
||||
priv->top = top;
|
||||
g_object_notify (gobject, "top");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->left != left)
|
||||
{
|
||||
priv->left = left;
|
||||
g_object_notify (gobject, "left");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->width != width)
|
||||
{
|
||||
priv->width = width;
|
||||
g_object_notify (gobject, "width");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->height != height)
|
||||
{
|
||||
priv->height = height;
|
||||
g_object_notify (gobject, "height");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
|
||||
|
||||
g_object_thaw_notify (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StSubtexture *frame = ST_SUBTEXTURE (gobject);
|
||||
StSubtexturePrivate *priv = frame->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT_TEXTURE:
|
||||
st_subtexture_set_parent_texture (frame,
|
||||
g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TOP:
|
||||
st_subtexture_set_frame_internal (frame,
|
||||
priv->left,
|
||||
g_value_get_int (value),
|
||||
priv->width,
|
||||
priv->height);
|
||||
break;
|
||||
|
||||
case PROP_LEFT:
|
||||
st_subtexture_set_frame_internal (frame,
|
||||
g_value_get_int (value),
|
||||
priv->top,
|
||||
priv->width,
|
||||
priv->height);
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
st_subtexture_set_frame_internal (frame,
|
||||
priv->left,
|
||||
priv->top,
|
||||
g_value_get_int (value),
|
||||
priv->height);
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
st_subtexture_set_frame_internal (frame,
|
||||
priv->left,
|
||||
priv->top,
|
||||
priv->width,
|
||||
g_value_get_int (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT_TEXTURE:
|
||||
g_value_set_object (value, priv->parent_texture);
|
||||
break;
|
||||
|
||||
case PROP_LEFT:
|
||||
g_value_set_int (value, priv->left);
|
||||
break;
|
||||
|
||||
case PROP_TOP:
|
||||
g_value_set_int (value, priv->top);
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
g_value_set_int (value, priv->width);
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
g_value_set_int (value, priv->height);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_dispose (GObject *gobject)
|
||||
{
|
||||
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
|
||||
|
||||
if (priv->parent_texture)
|
||||
{
|
||||
g_object_unref (priv->parent_texture);
|
||||
priv->parent_texture = NULL;
|
||||
}
|
||||
|
||||
if (priv->material)
|
||||
{
|
||||
cogl_material_unref (priv->material);
|
||||
priv->material = COGL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_subtexture_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_class_init (StSubtextureClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (StSubtexturePrivate));
|
||||
|
||||
actor_class->get_preferred_width =
|
||||
st_subtexture_get_preferred_width;
|
||||
actor_class->get_preferred_height =
|
||||
st_subtexture_get_preferred_height;
|
||||
actor_class->realize = st_subtexture_realize;
|
||||
actor_class->unrealize = st_subtexture_unrealize;
|
||||
actor_class->paint = st_subtexture_paint;
|
||||
|
||||
gobject_class->set_property = st_subtexture_set_property;
|
||||
gobject_class->get_property = st_subtexture_get_property;
|
||||
gobject_class->dispose = st_subtexture_dispose;
|
||||
|
||||
pspec = g_param_spec_object ("parent-texture",
|
||||
"Parent Texture",
|
||||
"The parent ClutterTexture",
|
||||
CLUTTER_TYPE_TEXTURE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT);
|
||||
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("left",
|
||||
"Left",
|
||||
"Left offset",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("top",
|
||||
"Top",
|
||||
"Top offset",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("width",
|
||||
"Width",
|
||||
"Width",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("height",
|
||||
"Height",
|
||||
"Height",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_HEIGHT, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_subtexture_init (StSubtexture *self)
|
||||
{
|
||||
StSubtexturePrivate *priv;
|
||||
|
||||
self->priv = priv = ST_SUBTEXTURE_GET_PRIVATE (self);
|
||||
|
||||
priv->material = COGL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_subtexture_new:
|
||||
* @texture: a #ClutterTexture or %NULL
|
||||
* @left: left
|
||||
* @top: top
|
||||
* @width: width
|
||||
* @height: height
|
||||
*
|
||||
* A #StSubtexture is a specialized texture that efficiently clones
|
||||
* an area of the given @texture while keeping preserving portions of the
|
||||
* same texture.
|
||||
*
|
||||
* A #StSubtexture can be used to make a rectangular texture fit a
|
||||
* given size without stretching its borders.
|
||||
*
|
||||
* Return value: the newly created #StSubtexture
|
||||
*/
|
||||
ClutterActor*
|
||||
st_subtexture_new (ClutterTexture *texture,
|
||||
gint left,
|
||||
gint top,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
|
||||
|
||||
return g_object_new (ST_TYPE_SUBTEXTURE,
|
||||
"parent-texture", texture,
|
||||
"top", top,
|
||||
"left", left,
|
||||
"width", width,
|
||||
"height", height,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_subtexture_get_parent_texture:
|
||||
* @frame: A #StSubtexture
|
||||
*
|
||||
* Return the texture used by the #StSubtexture
|
||||
*
|
||||
* Returns: (transfer none): a #ClutterTexture owned by the #StSubtexture
|
||||
*/
|
||||
ClutterTexture *
|
||||
st_subtexture_get_parent_texture (StSubtexture *frame)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_SUBTEXTURE (frame), NULL);
|
||||
|
||||
return frame->priv->parent_texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_subtexture_set_parent_texture:
|
||||
* @frame: A #StSubtexture
|
||||
* @texture: A #ClutterTexture
|
||||
*
|
||||
* Set the #ClutterTexture used by this #StSubtexture
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_subtexture_set_parent_texture (StSubtexture *frame,
|
||||
ClutterTexture *texture)
|
||||
{
|
||||
StSubtexturePrivate *priv;
|
||||
gboolean was_visible;
|
||||
|
||||
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
|
||||
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
|
||||
|
||||
priv = frame->priv;
|
||||
|
||||
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
|
||||
|
||||
if (priv->parent_texture == texture)
|
||||
return;
|
||||
|
||||
if (priv->parent_texture)
|
||||
{
|
||||
g_object_unref (priv->parent_texture);
|
||||
priv->parent_texture = NULL;
|
||||
|
||||
if (was_visible)
|
||||
clutter_actor_hide (CLUTTER_ACTOR (frame));
|
||||
}
|
||||
|
||||
if (texture)
|
||||
{
|
||||
priv->parent_texture = g_object_ref (texture);
|
||||
|
||||
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
|
||||
clutter_actor_show (CLUTTER_ACTOR (frame));
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
|
||||
|
||||
g_object_notify (G_OBJECT (frame), "parent-texture");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_subtexture_set_frame:
|
||||
* @frame: A #StSubtexture
|
||||
* @left: left
|
||||
* @top: top
|
||||
* @width: width
|
||||
* @height: height
|
||||
*
|
||||
* Set the frame of the subtexture
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_subtexture_set_frame (StSubtexture *frame,
|
||||
gint left,
|
||||
gint top,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
|
||||
|
||||
st_subtexture_set_frame_internal (frame, left, top, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_subtexture_get_frame:
|
||||
* @frame: A #StSubtexture
|
||||
* @left: left
|
||||
* @top: top
|
||||
* @width: width
|
||||
* @height: height
|
||||
*
|
||||
* Retrieve the current frame.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_subtexture_get_frame (StSubtexture *frame,
|
||||
gint *left,
|
||||
gint *top,
|
||||
gint *width,
|
||||
gint *height)
|
||||
{
|
||||
StSubtexturePrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
|
||||
|
||||
priv = frame->priv;
|
||||
|
||||
if (top)
|
||||
*top = priv->top;
|
||||
|
||||
if (left)
|
||||
*left = priv->left;
|
||||
|
||||
if (width)
|
||||
*width = priv->width;
|
||||
|
||||
if (height)
|
||||
*height = priv->height;
|
||||
}
|
97
src/st/st-subtexture.h
Normal file
97
src/st/st-subtexture.h
Normal file
@ -0,0 +1,97 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-subtexture.h: Class to wrap a texture and "subframe" it.
|
||||
*
|
||||
* Based on
|
||||
* st-texture-frame.h: Expandible texture actor
|
||||
*
|
||||
* Copyright 2007, 2008 OpenedHand Ltd
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_SUBTEXTURE_H__
|
||||
#define __ST_SUBTEXTURE_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_SUBTEXTURE (st_subtexture_get_type ())
|
||||
#define ST_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SUBTEXTURE, StSubtexture))
|
||||
#define ST_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SUBTEXTURE, StSubtextureClass))
|
||||
#define ST_IS_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SUBTEXTURE))
|
||||
#define ST_IS_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SUBTEXTURE))
|
||||
#define ST_SUBTEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SUBTEXTURE, StSubtextureClass))
|
||||
|
||||
typedef struct _StSubtexture StSubtexture;
|
||||
typedef struct _StSubtexturePrivate StSubtexturePrivate;
|
||||
typedef struct _StSubtextureClass StSubtextureClass;
|
||||
|
||||
/**
|
||||
* StSubtexture:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StSubtexture
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterActor parent_instance;
|
||||
|
||||
StSubtexturePrivate *priv;
|
||||
};
|
||||
|
||||
struct _StSubtextureClass
|
||||
{
|
||||
ClutterActorClass parent_class;
|
||||
|
||||
/* padding for future expansion */
|
||||
void (*_st_box_1) (void);
|
||||
void (*_st_box_2) (void);
|
||||
void (*_st_box_3) (void);
|
||||
void (*_st_box_4) (void);
|
||||
};
|
||||
|
||||
GType st_subtexture_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor * st_subtexture_new (ClutterTexture *texture,
|
||||
gint top,
|
||||
gint left,
|
||||
gint width,
|
||||
gint height);
|
||||
void st_subtexture_set_parent_texture (StSubtexture *frame,
|
||||
ClutterTexture *texture);
|
||||
ClutterTexture *st_subtexture_get_parent_texture (StSubtexture *frame);
|
||||
void st_subtexture_set_frame (StSubtexture *frame,
|
||||
gint top,
|
||||
gint left,
|
||||
gint width,
|
||||
gint height);
|
||||
void st_subtexture_get_frame (StSubtexture *frame,
|
||||
gint *top,
|
||||
gint *left,
|
||||
gint *width,
|
||||
gint *height);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_SUBTEXTURE_H__ */
|
805
src/st/st-table-child.c
Normal file
805
src/st/st-table-child.c
Normal file
@ -0,0 +1,805 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-table-child.h: Table child implementation
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas.wood@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "st-private.h"
|
||||
#include "st-table-child.h"
|
||||
#include "st-table-private.h"
|
||||
#include <st/st-widget.h>
|
||||
#include <st/st-table.h>
|
||||
|
||||
/*
|
||||
* ClutterChildMeta Implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-table-child
|
||||
* @short_description: The child property store for #StTable
|
||||
*
|
||||
* The #ClutterChildMeta implementation for the #StTable container widget.
|
||||
*
|
||||
*/
|
||||
|
||||
enum {
|
||||
CHILD_PROP_0,
|
||||
|
||||
CHILD_PROP_COL,
|
||||
CHILD_PROP_ROW,
|
||||
CHILD_PROP_COL_SPAN,
|
||||
CHILD_PROP_ROW_SPAN,
|
||||
CHILD_PROP_X_EXPAND,
|
||||
CHILD_PROP_Y_EXPAND,
|
||||
CHILD_PROP_X_ALIGN,
|
||||
CHILD_PROP_Y_ALIGN,
|
||||
CHILD_PROP_X_FILL,
|
||||
CHILD_PROP_Y_FILL,
|
||||
CHILD_PROP_ALLOCATE_HIDDEN,
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StTableChild, st_table_child, CLUTTER_TYPE_CHILD_META);
|
||||
|
||||
static void
|
||||
table_child_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTableChild *child = ST_TABLE_CHILD (gobject);
|
||||
StTable *table = ST_TABLE (CLUTTER_CHILD_META(gobject)->container);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case CHILD_PROP_COL:
|
||||
child->col = g_value_get_int (value);
|
||||
_st_table_update_row_col (table, -1, child->col);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_ROW:
|
||||
child->row = g_value_get_int (value);
|
||||
_st_table_update_row_col (table, child->row, -1);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_COL_SPAN:
|
||||
child->col_span = g_value_get_int (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_ROW_SPAN:
|
||||
child->row_span = g_value_get_int (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_X_EXPAND:
|
||||
child->x_expand = g_value_get_boolean (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_Y_EXPAND:
|
||||
child->y_expand = g_value_get_boolean (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_X_ALIGN:
|
||||
child->x_align = g_value_get_double (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_Y_ALIGN:
|
||||
child->y_align = g_value_get_double (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_X_FILL:
|
||||
child->x_fill = g_value_get_boolean (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_Y_FILL:
|
||||
child->y_fill = g_value_get_boolean (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
case CHILD_PROP_ALLOCATE_HIDDEN:
|
||||
child->allocate_hidden = g_value_get_boolean (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
table_child_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTableChild *child = ST_TABLE_CHILD (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case CHILD_PROP_COL:
|
||||
g_value_set_int (value, child->col);
|
||||
break;
|
||||
case CHILD_PROP_ROW:
|
||||
g_value_set_int (value, child->row);
|
||||
break;
|
||||
case CHILD_PROP_COL_SPAN:
|
||||
g_value_set_int (value, child->col_span);
|
||||
break;
|
||||
case CHILD_PROP_ROW_SPAN:
|
||||
g_value_set_int (value, child->row_span);
|
||||
break;
|
||||
case CHILD_PROP_X_EXPAND:
|
||||
g_value_set_boolean (value, child->x_expand);
|
||||
break;
|
||||
case CHILD_PROP_Y_EXPAND:
|
||||
g_value_set_boolean (value, child->y_expand);
|
||||
break;
|
||||
case CHILD_PROP_X_ALIGN:
|
||||
g_value_set_double (value, child->x_align);
|
||||
break;
|
||||
case CHILD_PROP_Y_ALIGN:
|
||||
g_value_set_double (value, child->y_align);
|
||||
break;
|
||||
case CHILD_PROP_X_FILL:
|
||||
g_value_set_boolean (value, child->x_fill);
|
||||
break;
|
||||
case CHILD_PROP_Y_FILL:
|
||||
g_value_set_boolean (value, child->y_fill);
|
||||
break;
|
||||
case CHILD_PROP_ALLOCATE_HIDDEN:
|
||||
g_value_set_boolean (value, child->allocate_hidden);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_table_child_class_init (StTableChildClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
gobject_class->set_property = table_child_set_property;
|
||||
gobject_class->get_property = table_child_get_property;
|
||||
|
||||
pspec = g_param_spec_int ("col",
|
||||
"Column Number",
|
||||
"The column the widget resides in",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_COL, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("row",
|
||||
"Row Number",
|
||||
"The row the widget resides in",
|
||||
0, G_MAXINT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_ROW, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("row-span",
|
||||
"Row Span",
|
||||
"The number of rows the widget should span",
|
||||
1, G_MAXINT,
|
||||
1,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_ROW_SPAN, pspec);
|
||||
|
||||
pspec = g_param_spec_int ("col-span",
|
||||
"Column Span",
|
||||
"The number of columns the widget should span",
|
||||
1, G_MAXINT,
|
||||
1,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_COL_SPAN, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("x-expand",
|
||||
"X Expand",
|
||||
"Whether the child should receive priority "
|
||||
"when the container is allocating spare space "
|
||||
"on the horizontal axis",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_X_EXPAND, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("y-expand",
|
||||
"Y Expand",
|
||||
"Whether the child should receive priority "
|
||||
"when the container is allocating spare space "
|
||||
"on the vertical axis",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_Y_EXPAND, pspec);
|
||||
|
||||
pspec = g_param_spec_double ("x-align",
|
||||
"X Alignment",
|
||||
"X alignment of the widget within the cell",
|
||||
0, 1,
|
||||
0.5,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_X_ALIGN, pspec);
|
||||
|
||||
pspec = g_param_spec_double ("y-align",
|
||||
"Y Alignment",
|
||||
"Y alignment of the widget within the cell",
|
||||
0, 1,
|
||||
0.5,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_Y_ALIGN, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("x-fill",
|
||||
"X Fill",
|
||||
"Whether the child should be allocated its "
|
||||
"entire available space, or whether it should "
|
||||
"be squashed and aligned.",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_X_FILL, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("y-fill",
|
||||
"Y Fill",
|
||||
"Whether the child should be allocated its "
|
||||
"entire available space, or whether it should "
|
||||
"be squashed and aligned.",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_Y_FILL, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean ("allocate-hidden",
|
||||
"Allocate Hidden",
|
||||
"Whether the child should be allocate even "
|
||||
"if it is hidden",
|
||||
TRUE,
|
||||
ST_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_property (gobject_class, CHILD_PROP_ALLOCATE_HIDDEN, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_table_child_init (StTableChild *self)
|
||||
{
|
||||
self->col_span = 1;
|
||||
self->row_span = 1;
|
||||
|
||||
self->x_align = 0.5;
|
||||
self->y_align = 0.5;
|
||||
|
||||
self->x_expand = TRUE;
|
||||
self->y_expand = TRUE;
|
||||
|
||||
self->x_fill = TRUE;
|
||||
self->y_fill = TRUE;
|
||||
|
||||
self->allocate_hidden = TRUE;
|
||||
}
|
||||
|
||||
static StTableChild*
|
||||
get_child_meta (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
meta = (StTableChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_col_span:
|
||||
* @table: an #StTable
|
||||
* @child: a #ClutterActor
|
||||
*
|
||||
* Get the column span of the child. Defaults to 1.
|
||||
*
|
||||
* Returns: the column span of the child
|
||||
*/
|
||||
gint
|
||||
st_table_child_get_col_span (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->col_span;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_col_span:
|
||||
* @table: An #StTable
|
||||
* @child: An #ClutterActor
|
||||
* @span: The number of columns to span
|
||||
*
|
||||
* Set the column span of the child.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_col_span (StTable *table,
|
||||
ClutterActor *child,
|
||||
gint span)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
g_return_if_fail (span > 1);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->col_span = span;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_row_span:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the row span of the child. Defaults to 1.
|
||||
*
|
||||
* Returns: the row span of the child
|
||||
*/
|
||||
gint
|
||||
st_table_child_get_row_span (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->row_span;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_row_span:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @span: the number of rows to span
|
||||
*
|
||||
* Set the row span of the child.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_row_span (StTable *table,
|
||||
ClutterActor *child,
|
||||
gint span)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
g_return_if_fail (span > 1);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->row_span = span;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_x_fill:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the x-fill state of the child
|
||||
*
|
||||
* Returns: #TRUE if the child is set to x-fill
|
||||
*/
|
||||
gboolean
|
||||
st_table_child_get_x_fill (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->x_fill;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_x_fill:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @fill: the fill state
|
||||
*
|
||||
* Set the fill state of the child on the x-axis. This will cause the child to
|
||||
* be allocated the maximum available space.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_x_fill (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean fill)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->x_fill = fill;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* st_table_child_get_y_fill:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the y-fill state of the child
|
||||
*
|
||||
* Returns: #TRUE if the child is set to y-fill
|
||||
*/
|
||||
gboolean
|
||||
st_table_child_get_y_fill (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->y_fill;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_y_fill:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @fill: the fill state
|
||||
*
|
||||
* Set the fill state of the child on the y-axis. This will cause the child to
|
||||
* be allocated the maximum available space.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_y_fill (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean fill)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->y_fill = fill;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_x_expand:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the x-expand property of the child
|
||||
*
|
||||
* Returns: #TRUE if the child is set to x-expand
|
||||
*/
|
||||
gboolean
|
||||
st_table_child_get_x_expand (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->x_expand;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_x_expand:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @expand: the new value of the x expand child property
|
||||
*
|
||||
* Set x-expand on the child. This causes the column which the child
|
||||
* resides in to be allocated any extra space if the allocation of the table is
|
||||
* larger than the preferred size.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_x_expand (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean expand)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->x_expand = expand;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_y_expand:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @expand: the new value of the y-expand child property
|
||||
*
|
||||
* Set y-expand on the child. This causes the row which the child
|
||||
* resides in to be allocated any extra space if the allocation of the table is
|
||||
* larger than the preferred size.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_y_expand (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean expand)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
meta->y_expand = expand;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_y_expand:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the y-expand property of the child.
|
||||
*
|
||||
* Returns: #TRUE if the child is set to y-expand
|
||||
*/
|
||||
gboolean
|
||||
st_table_child_get_y_expand (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->y_expand;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_x_align:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the x-align value of the child
|
||||
*
|
||||
* Returns: An #StAlign value
|
||||
*/
|
||||
StAlign
|
||||
st_table_child_get_x_align (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
if (meta->x_align == 0.0)
|
||||
return ST_ALIGN_START;
|
||||
else if (meta->x_align == 1.0)
|
||||
return ST_ALIGN_END;
|
||||
else
|
||||
return ST_ALIGN_MIDDLE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_x_align:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @align: A #StAlign value
|
||||
*
|
||||
* Set the alignment of the child within its cell. This will only have an effect
|
||||
* if the the x-fill property is FALSE.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_x_align (StTable *table,
|
||||
ClutterActor *child,
|
||||
StAlign align)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
switch (align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
meta->x_align = 0.0;
|
||||
break;
|
||||
case ST_ALIGN_MIDDLE:
|
||||
meta->x_align = 0.5;
|
||||
break;
|
||||
case ST_ALIGN_END:
|
||||
meta->x_align = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_y_align:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Get the y-align value of the child
|
||||
*
|
||||
* Returns: An #StAlign value
|
||||
*/
|
||||
StAlign
|
||||
st_table_child_get_y_align (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), 0);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
if (meta->y_align == 0.0)
|
||||
return ST_ALIGN_START;
|
||||
else if (meta->y_align == 1.0)
|
||||
return ST_ALIGN_END;
|
||||
else
|
||||
return ST_ALIGN_MIDDLE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_y_align:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @align: A #StAlign value
|
||||
*
|
||||
* Set the value of the y-align property. This will only have an effect if
|
||||
* y-fill value is set to FALSE.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_table_child_set_y_align (StTable *table,
|
||||
ClutterActor *child,
|
||||
StAlign align)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
switch (align)
|
||||
{
|
||||
case ST_ALIGN_START:
|
||||
meta->y_align = 0.0;
|
||||
break;
|
||||
case ST_ALIGN_MIDDLE:
|
||||
meta->y_align = 0.5;
|
||||
break;
|
||||
case ST_ALIGN_END:
|
||||
meta->y_align = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_set_allocate_hidden:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
* @value: #TRUE if the actor should be allocated when hidden
|
||||
*
|
||||
* Set whether the child should be allocate even if it is hidden
|
||||
*/
|
||||
void
|
||||
st_table_child_set_allocate_hidden (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean value)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_if_fail (ST_IS_TABLE (table));
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
if (meta->allocate_hidden != value)
|
||||
{
|
||||
meta->allocate_hidden = value;
|
||||
|
||||
clutter_actor_queue_relayout (child);
|
||||
|
||||
g_object_notify (G_OBJECT (meta), "allocate-hidden");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_table_child_get_allocate_hidden:
|
||||
* @table: A #StTable
|
||||
* @child: A #ClutterActor
|
||||
*
|
||||
* Determine if the child is allocated even if it is hidden
|
||||
*
|
||||
* Returns: #TRUE if the actor is allocated when hidden
|
||||
*/
|
||||
gboolean
|
||||
st_table_child_get_allocate_hidden (StTable *table,
|
||||
ClutterActor *child)
|
||||
{
|
||||
StTableChild *meta;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TABLE (table), TRUE);
|
||||
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), TRUE);
|
||||
|
||||
meta = get_child_meta (table, child);
|
||||
|
||||
return meta->allocate_hidden;
|
||||
}
|
129
src/st/st-table-child.h
Normal file
129
src/st/st-table-child.h
Normal file
@ -0,0 +1,129 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-table-child.h: Table child implementation
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_TABLE_CHILD_H__
|
||||
#define __ST_TABLE_CHILD_H__
|
||||
|
||||
#include <st/st-types.h>
|
||||
#include <st/st-widget.h>
|
||||
#include <st/st-table.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_TABLE_CHILD (st_table_child_get_type ())
|
||||
#define ST_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE_CHILD, StTableChild))
|
||||
#define ST_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE_CHILD))
|
||||
#define ST_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE_CHILD, StTableChildClass))
|
||||
#define ST_IS_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE_CHILD))
|
||||
#define ST_TABLE_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE_CHILD, StTableChildClass))
|
||||
|
||||
typedef struct _StTableChild StTableChild;
|
||||
typedef struct _StTableChildClass StTableChildClass;
|
||||
|
||||
/**
|
||||
* StTableChild:
|
||||
*
|
||||
* The contents of the this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StTableChild
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterChildMeta parent_instance;
|
||||
|
||||
gint col;
|
||||
gint row;
|
||||
gint col_span;
|
||||
gint row_span;
|
||||
gdouble x_align;
|
||||
gdouble y_align;
|
||||
guint allocate_hidden : 1;
|
||||
guint x_expand : 1;
|
||||
guint y_expand : 1;
|
||||
guint x_fill : 1;
|
||||
guint y_fill : 1;
|
||||
};
|
||||
|
||||
|
||||
struct _StTableChildClass
|
||||
{
|
||||
ClutterChildMetaClass parent_class;
|
||||
};
|
||||
|
||||
GType st_table_child_get_type (void) G_GNUC_CONST;
|
||||
|
||||
gint st_table_child_get_col_span (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_col_span (StTable *table,
|
||||
ClutterActor *child,
|
||||
gint span);
|
||||
gint st_table_child_get_row_span (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_row_span (StTable *table,
|
||||
ClutterActor *child,
|
||||
gint span);
|
||||
gboolean st_table_child_get_x_fill (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_x_fill (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean fill);
|
||||
gboolean st_table_child_get_y_fill (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_y_fill (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean fill);
|
||||
gboolean st_table_child_get_x_expand (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_x_expand (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean expand);
|
||||
gboolean st_table_child_get_y_expand (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_y_expand (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean expand);
|
||||
StAlign st_table_child_get_x_align (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_x_align (StTable *table,
|
||||
ClutterActor *child,
|
||||
StAlign align);
|
||||
StAlign st_table_child_get_y_align (StTable *table,
|
||||
ClutterActor *child);
|
||||
void st_table_child_set_y_align (StTable *table,
|
||||
ClutterActor *child,
|
||||
StAlign align);
|
||||
void st_table_child_set_allocate_hidden (StTable *table,
|
||||
ClutterActor *child,
|
||||
gboolean value);
|
||||
gboolean st_table_child_get_allocate_hidden (StTable *table,
|
||||
ClutterActor *child);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TABLE_H__ */
|
36
src/st/st-table-private.h
Normal file
36
src/st/st-table-private.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* st-private-private.h: Private declarations for StTable
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ST_TABLE_PRIVATE_H__
|
||||
#define __ST_TABLE_PRIVATE_H__
|
||||
|
||||
#include "st-table.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void _st_table_update_row_col (StTable *table,
|
||||
gint row,
|
||||
gint col);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TABLE_PRIVATE_H__ */
|
1257
src/st/st-table.c
Normal file
1257
src/st/st-table.c
Normal file
File diff suppressed because it is too large
Load Diff
95
src/st/st-table.h
Normal file
95
src/st/st-table.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-table.h: Table layout widget
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_TABLE_H__
|
||||
#define __ST_TABLE_H__
|
||||
|
||||
#include <st/st-types.h>
|
||||
#include <st/st-widget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* StTableChildOptions:
|
||||
* @ST_KEEP_ASPECT_RATIO: whether to respect the widget's aspect ratio
|
||||
* @ST_X_EXPAND: whether to allocate extra space on the widget's x-axis
|
||||
* @ST_Y_EXPAND: whether to allocate extra space on the widget's y-axis
|
||||
* @ST_X_FILL: whether to stretch the child to fill the cell horizontally
|
||||
* @ST_Y_FILL: whether to stretch the child to fill the cell vertically
|
||||
*
|
||||
* Denotes the child properties an StTable child will have.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
ST_KEEP_ASPECT_RATIO = 1 << 0,
|
||||
ST_X_EXPAND = 1 << 1,
|
||||
ST_Y_EXPAND = 1 << 2,
|
||||
ST_X_FILL = 1 << 3,
|
||||
ST_Y_FILL = 1 << 4
|
||||
} StTableChildOptions;
|
||||
|
||||
#define ST_TYPE_TABLE (st_table_get_type ())
|
||||
#define ST_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE, StTable))
|
||||
#define ST_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE))
|
||||
#define ST_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE, StTableClass))
|
||||
#define ST_IS_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE))
|
||||
#define ST_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE, StTableClass))
|
||||
|
||||
typedef struct _StTable StTable;
|
||||
typedef struct _StTablePrivate StTablePrivate;
|
||||
typedef struct _StTableClass StTableClass;
|
||||
|
||||
/**
|
||||
* StTable:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
struct _StTable
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent_instance;
|
||||
|
||||
StTablePrivate *priv;
|
||||
};
|
||||
|
||||
struct _StTableClass
|
||||
{
|
||||
StWidgetClass parent_class;
|
||||
};
|
||||
|
||||
GType st_table_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StWidget* st_table_new (void);
|
||||
|
||||
gint st_table_get_row_count (StTable *table);
|
||||
gint st_table_get_column_count (StTable *table);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TABLE_H__ */
|
451
src/st/st-texture-cache.c
Normal file
451
src/st/st-texture-cache.c
Normal file
@ -0,0 +1,451 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-widget.h: Base class for St actors
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-texture-cache
|
||||
* @short_description: A per-process store to cache textures
|
||||
*
|
||||
* #StTextureCache allows an application to re-use an previously loaded
|
||||
* textures.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "st-texture-cache.h"
|
||||
#include "st-marshal.h"
|
||||
#include "st-private.h"
|
||||
#include "st-subtexture.h"
|
||||
G_DEFINE_TYPE (StTextureCache, st_texture_cache, G_TYPE_OBJECT)
|
||||
|
||||
#define TEXTURE_CACHE_PRIVATE(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_TEXTURE_CACHE, StTextureCachePrivate))
|
||||
|
||||
typedef struct _StTextureCachePrivate StTextureCachePrivate;
|
||||
|
||||
struct _StTextureCachePrivate
|
||||
{
|
||||
GHashTable *cache;
|
||||
};
|
||||
|
||||
typedef struct FinalizedClosure
|
||||
{
|
||||
gchar *path;
|
||||
StTextureCache *cache;
|
||||
} FinalizedClosure;
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
};
|
||||
|
||||
static StTextureCache* __cache_singleton = NULL;
|
||||
|
||||
/*
|
||||
* Convention: posX with a value of -1 indicates whole texture
|
||||
*/
|
||||
typedef struct StTextureCacheItem {
|
||||
char filename[256];
|
||||
int width, height;
|
||||
int posX, posY;
|
||||
ClutterActor *ptr;
|
||||
} StTextureCacheItem;
|
||||
|
||||
static StTextureCacheItem *
|
||||
st_texture_cache_item_new (void)
|
||||
{
|
||||
return g_slice_new0 (StTextureCacheItem);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_item_free (StTextureCacheItem *item)
|
||||
{
|
||||
g_slice_free (StTextureCacheItem, item);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (prop_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (prop_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_dispose (GObject *object)
|
||||
{
|
||||
if (G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose)
|
||||
G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_finalize (GObject *object)
|
||||
{
|
||||
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
|
||||
|
||||
if (priv->cache)
|
||||
{
|
||||
g_hash_table_unref (priv->cache);
|
||||
priv->cache = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_class_init (StTextureCacheClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StTextureCachePrivate));
|
||||
|
||||
object_class->get_property = st_texture_cache_get_property;
|
||||
object_class->set_property = st_texture_cache_set_property;
|
||||
object_class->dispose = st_texture_cache_dispose;
|
||||
object_class->finalize = st_texture_cache_finalize;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_cache_init (StTextureCache *self)
|
||||
{
|
||||
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||||
|
||||
priv->cache = g_hash_table_new_full (g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
NULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_cache_get_default:
|
||||
*
|
||||
* Returns the default texture cache. This is owned by St and should not be
|
||||
* unreferenced or freed.
|
||||
*
|
||||
* Returns: (transfer none): a StTextureCache
|
||||
*/
|
||||
StTextureCache*
|
||||
st_texture_cache_get_default (void)
|
||||
{
|
||||
if (G_UNLIKELY (__cache_singleton == NULL))
|
||||
__cache_singleton = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
|
||||
|
||||
return __cache_singleton;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
on_texure_finalized (gpointer data,
|
||||
GObject *where_the_object_was)
|
||||
{
|
||||
FinalizedClosure *closure = (FinalizedClosure *) data;
|
||||
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
|
||||
|
||||
g_hash_table_remove (priv->cache, closure->path);
|
||||
|
||||
g_free(closure->path);
|
||||
g_free(closure);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* st_texture_cache_get_size:
|
||||
* @self: A #StTextureCache
|
||||
*
|
||||
* Returns the number of items in the texture cache
|
||||
*
|
||||
* Returns: the current size of the cache
|
||||
*/
|
||||
gint
|
||||
st_texture_cache_get_size (StTextureCache *self)
|
||||
{
|
||||
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||||
|
||||
return g_hash_table_size (priv->cache);
|
||||
}
|
||||
|
||||
static void
|
||||
add_texture_to_cache (StTextureCache *self,
|
||||
const gchar *path,
|
||||
StTextureCacheItem *item)
|
||||
{
|
||||
/* FinalizedClosure *closure; */
|
||||
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||||
|
||||
g_hash_table_insert (priv->cache, g_strdup (path), item);
|
||||
|
||||
#if 0
|
||||
/* Make sure we can remove from hash */
|
||||
closure = g_new0 (FinalizedClosure, 1);
|
||||
closure->path = g_strdup (path);
|
||||
closure->cache = self;
|
||||
|
||||
g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* NOTE: you should unref the returned texture when not needed */
|
||||
|
||||
/**
|
||||
* st_texture_cache_get_texture:
|
||||
* @self: A #StTextureCache
|
||||
* @path: A path to a image file
|
||||
*
|
||||
* Create a new ClutterTexture with the specified image. Adds the image to the
|
||||
* cache if the image had not been previously loaded. Subsequent calls with
|
||||
* the same image path will return a new ClutterTexture with the previously
|
||||
* loaded image.
|
||||
*
|
||||
* Returns: (transfer none): a newly created ClutterTexture
|
||||
*/
|
||||
ClutterTexture*
|
||||
st_texture_cache_get_texture (StTextureCache *self,
|
||||
const gchar *path)
|
||||
{
|
||||
ClutterActor *texture;
|
||||
CoglHandle *handle;
|
||||
StTextureCachePrivate *priv;
|
||||
StTextureCacheItem *item;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
|
||||
priv = TEXTURE_CACHE_PRIVATE (self);
|
||||
|
||||
item = g_hash_table_lookup (priv->cache, path);
|
||||
|
||||
if (item && item->posX != -1)
|
||||
{
|
||||
GError *err = NULL;
|
||||
/*
|
||||
* We have a cache hit, but it's for a partial texture. The only
|
||||
* sane option is to read it from disk and just don't cache it
|
||||
* at all.
|
||||
*/
|
||||
return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
|
||||
}
|
||||
if (!item)
|
||||
{
|
||||
GError *err = NULL;
|
||||
|
||||
item = st_texture_cache_item_new ();
|
||||
item->posX = -1;
|
||||
item->posY = -1;
|
||||
item->ptr = clutter_texture_new_from_file (path, &err);
|
||||
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
|
||||
&item->width, &item->height);
|
||||
|
||||
if (!item->ptr)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
g_warning ("Error loading image: %s", err->message);
|
||||
g_error_free (err);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
add_texture_to_cache (self, path, item);
|
||||
}
|
||||
|
||||
texture = clutter_texture_new ();
|
||||
handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
|
||||
clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
|
||||
|
||||
return (ClutterTexture*) texture;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* st_texture_cache_get_actor:
|
||||
* @self: A #StTextureCache
|
||||
* @path: A path to a image file
|
||||
*
|
||||
* Create a new ClutterSubTexture with the specified image. Adds the image to the
|
||||
* cache if the image had not been previously loaded. Subsequent calls with
|
||||
* the same image path will return a new ClutterTexture with the previously
|
||||
* loaded image.
|
||||
*
|
||||
* Use this function if all you need is an actor for drawing.
|
||||
*
|
||||
* Returns: (transfer none): a newly created ClutterTexture
|
||||
*/
|
||||
ClutterActor*
|
||||
st_texture_cache_get_actor (StTextureCache *self,
|
||||
const gchar *path)
|
||||
{
|
||||
StTextureCachePrivate *priv;
|
||||
StTextureCacheItem *item;
|
||||
GError *err = NULL;
|
||||
|
||||
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
priv = TEXTURE_CACHE_PRIVATE (self);
|
||||
|
||||
|
||||
item = g_hash_table_lookup (priv->cache, path);
|
||||
|
||||
if (item)
|
||||
{
|
||||
int posX = item->posX;
|
||||
int posY = item->posY;
|
||||
if (posX == -1)
|
||||
posX = 0;
|
||||
if (posY == -1)
|
||||
posY = 0;
|
||||
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
|
||||
item->width, item->height);
|
||||
}
|
||||
|
||||
item = st_texture_cache_item_new ();
|
||||
item->posX = -1;
|
||||
item->posY = -1;
|
||||
item->ptr = clutter_texture_new_from_file (path, &err);
|
||||
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
|
||||
&item->width, &item->height);
|
||||
|
||||
if (!item->ptr)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
g_warning ("Error loading image: %s", err->message);
|
||||
g_error_free (err);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
add_texture_to_cache (self, path, item);
|
||||
|
||||
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
|
||||
item->height);
|
||||
}
|
||||
|
||||
void
|
||||
st_texture_cache_load_cache (StTextureCache *self,
|
||||
const gchar *filename)
|
||||
{
|
||||
FILE *file;
|
||||
StTextureCacheItem *element, head;
|
||||
int ret;
|
||||
ClutterActor *actor;
|
||||
GError *error = NULL;
|
||||
StTextureCachePrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_TEXTURE_CACHE (self));
|
||||
g_return_if_fail (filename != NULL);
|
||||
|
||||
priv = TEXTURE_CACHE_PRIVATE (self);
|
||||
|
||||
file = fopen(filename, "rm");
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
ret = fread (&head, sizeof(StTextureCacheItem), 1, file);
|
||||
if (ret < 0)
|
||||
{
|
||||
fclose (file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if we already if this texture in the cache */
|
||||
if (g_hash_table_lookup (priv->cache, head.filename))
|
||||
{
|
||||
/* skip it, we're done */
|
||||
fclose (file);
|
||||
return;
|
||||
}
|
||||
|
||||
actor = clutter_texture_new_from_file (head.filename, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_critical (G_STRLOC ": Error opening cache image file: %s",
|
||||
error->message);
|
||||
g_clear_error (&error);
|
||||
fclose (file);
|
||||
return;
|
||||
}
|
||||
|
||||
element = st_texture_cache_item_new ();
|
||||
element->posX = -1;
|
||||
element->posY = -1;
|
||||
element->ptr = actor;
|
||||
strncpy (element->filename, head.filename, 256);
|
||||
clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
|
||||
&element->width, &element->height);
|
||||
g_hash_table_insert (priv->cache, element->filename, element);
|
||||
|
||||
while (!feof (file))
|
||||
{
|
||||
element = st_texture_cache_item_new ();
|
||||
ret = fread (element, sizeof (StTextureCacheItem), 1, file);
|
||||
if (ret < 1)
|
||||
{
|
||||
/* end of file */
|
||||
st_texture_cache_item_free (element);
|
||||
break;
|
||||
}
|
||||
|
||||
element->ptr = actor;
|
||||
|
||||
if (g_hash_table_lookup (priv->cache, element->filename))
|
||||
{
|
||||
/* file is already in the cache.... */
|
||||
st_texture_cache_item_free (element);
|
||||
} else {
|
||||
g_hash_table_insert (priv->cache, element->filename, element);
|
||||
}
|
||||
}
|
||||
}
|
96
src/st/st-texture-cache.h
Normal file
96
src/st/st-texture-cache.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-texture-cache.h: Cached textures object
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ST_TEXTURE_CACHE
|
||||
#define _ST_TEXTURE_CACHE
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_TEXTURE_CACHE st_texture_cache_get_type()
|
||||
|
||||
#define ST_TEXTURE_CACHE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
ST_TYPE_TEXTURE_CACHE, StTextureCache))
|
||||
|
||||
#define ST_TEXTURE_CACHE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
|
||||
|
||||
#define ST_IS_TEXTURE_CACHE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
ST_TYPE_TEXTURE_CACHE))
|
||||
|
||||
#define ST_IS_TEXTURE_CACHE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
ST_TYPE_TEXTURE_CACHE))
|
||||
|
||||
#define ST_TEXTURE_CACHE_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
|
||||
|
||||
/**
|
||||
* StTextureCache:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
typedef struct {
|
||||
/*< private >*/
|
||||
GObject parent;
|
||||
} StTextureCache;
|
||||
|
||||
typedef struct {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* loaded) (StTextureCache *self,
|
||||
const gchar *path,
|
||||
ClutterTexture *texture);
|
||||
|
||||
void (* error_loading) (StTextureCache *self,
|
||||
GError *error);
|
||||
} StTextureCacheClass;
|
||||
|
||||
GType st_texture_cache_get_type (void);
|
||||
|
||||
StTextureCache* st_texture_cache_get_default (void);
|
||||
|
||||
ClutterTexture* st_texture_cache_get_texture (StTextureCache *self,
|
||||
const gchar *path);
|
||||
ClutterActor* st_texture_cache_get_actor (StTextureCache *self,
|
||||
const gchar *path);
|
||||
|
||||
gint st_texture_cache_get_size (StTextureCache *self);
|
||||
|
||||
void st_texture_cache_load_cache (StTextureCache *self,
|
||||
const char *filename);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _ST_TEXTURE_CACHE */
|
621
src/st/st-texture-frame.c
Normal file
621
src/st/st-texture-frame.c
Normal file
@ -0,0 +1,621 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-texture-frame.h: Expandible texture actor
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-texture-frame
|
||||
* @short_description: Stretch a texture to fit the entire allocation
|
||||
*
|
||||
* #StTextureFrame
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cogl/cogl.h>
|
||||
|
||||
#include "st-texture-frame.h"
|
||||
#include "st-private.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_PARENT_TEXTURE,
|
||||
|
||||
PROP_TOP,
|
||||
PROP_RIGHT,
|
||||
PROP_BOTTOM,
|
||||
PROP_LEFT
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StTextureFrame, st_texture_frame, CLUTTER_TYPE_ACTOR);
|
||||
|
||||
#define ST_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFramePrivate))
|
||||
|
||||
struct _StTextureFramePrivate
|
||||
{
|
||||
ClutterTexture *parent_texture;
|
||||
|
||||
gfloat top;
|
||||
gfloat right;
|
||||
gfloat bottom;
|
||||
gfloat left;
|
||||
};
|
||||
|
||||
static void
|
||||
st_texture_frame_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
|
||||
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
{
|
||||
if (min_width_p)
|
||||
*min_width_p = 0;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutterActorClass *klass;
|
||||
|
||||
/* by directly querying the parent texture's class implementation
|
||||
* we are going around any override mechanism the parent texture
|
||||
* might have in place, and we ask directly for the original
|
||||
* preferred width
|
||||
*/
|
||||
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
|
||||
klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
|
||||
for_height,
|
||||
min_width_p,
|
||||
natural_width_p);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_get_preferred_height (ClutterActor *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
|
||||
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
{
|
||||
if (min_height_p)
|
||||
*min_height_p = 0;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutterActorClass *klass;
|
||||
|
||||
/* by directly querying the parent texture's class implementation
|
||||
* we are going around any override mechanism the parent texture
|
||||
* might have in place, and we ask directly for the original
|
||||
* preferred height
|
||||
*/
|
||||
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
|
||||
klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
|
||||
for_width,
|
||||
min_height_p,
|
||||
natural_height_p);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_paint (ClutterActor *self)
|
||||
{
|
||||
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
|
||||
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
|
||||
CoglHandle cogl_material = COGL_INVALID_HANDLE;
|
||||
ClutterActorBox box = { 0, };
|
||||
gfloat width, height;
|
||||
gfloat tex_width, tex_height;
|
||||
gfloat ex, ey;
|
||||
gfloat tx1, ty1, tx2, ty2;
|
||||
guint8 opacity;
|
||||
|
||||
/* no need to paint stuff if we don't have a texture */
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
return;
|
||||
|
||||
/* parent texture may have been hidden, so need to make sure it gets
|
||||
* realized
|
||||
*/
|
||||
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
|
||||
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
|
||||
|
||||
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
|
||||
if (cogl_texture == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
|
||||
if (cogl_material == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
tex_width = cogl_texture_get_width (cogl_texture);
|
||||
tex_height = cogl_texture_get_height (cogl_texture);
|
||||
|
||||
clutter_actor_get_allocation_box (self, &box);
|
||||
width = box.x2 - box.x1;
|
||||
height = box.y2 - box.y1;
|
||||
|
||||
tx1 = priv->left / tex_width;
|
||||
tx2 = (tex_width - priv->right) / tex_width;
|
||||
ty1 = priv->top / tex_height;
|
||||
ty2 = (tex_height - priv->bottom) / tex_height;
|
||||
|
||||
ex = width - priv->right;
|
||||
if (ex < 0)
|
||||
ex = priv->right; /* FIXME ? */
|
||||
|
||||
ey = height - priv->bottom;
|
||||
if (ey < 0)
|
||||
ey = priv->bottom; /* FIXME ? */
|
||||
|
||||
opacity = clutter_actor_get_paint_opacity (self);
|
||||
|
||||
/* Paint using the parent texture's material. It should already have
|
||||
the cogl texture set as the first layer */
|
||||
/* NB: for correct blending we need set a preumultiplied color here: */
|
||||
cogl_material_set_color4ub (cogl_material,
|
||||
opacity, opacity, opacity, opacity);
|
||||
cogl_set_source (cogl_material);
|
||||
|
||||
{
|
||||
GLfloat rectangles[] =
|
||||
{
|
||||
/* top left corner */
|
||||
0, 0, priv->left, priv->top,
|
||||
0.0, 0.0,
|
||||
tx1, ty1,
|
||||
|
||||
/* top middle */
|
||||
priv->left, 0, ex, priv->top,
|
||||
tx1, 0.0,
|
||||
tx2, ty1,
|
||||
|
||||
/* top right */
|
||||
ex, 0, width, priv->top,
|
||||
tx2, 0.0,
|
||||
1.0, ty1,
|
||||
|
||||
/* mid left */
|
||||
0, priv->top, priv->left, ey,
|
||||
0.0, ty1,
|
||||
tx1, ty2,
|
||||
|
||||
/* center */
|
||||
priv->left, priv->top, ex, ey,
|
||||
tx1, ty1,
|
||||
tx2, ty2,
|
||||
|
||||
/* mid right */
|
||||
ex, priv->top, width, ey,
|
||||
tx2, ty1,
|
||||
1.0, ty2,
|
||||
|
||||
/* bottom left */
|
||||
0, ey, priv->left, height,
|
||||
0.0, ty2,
|
||||
tx1, 1.0,
|
||||
|
||||
/* bottom center */
|
||||
priv->left, ey, ex, height,
|
||||
tx1, ty2,
|
||||
tx2, 1.0,
|
||||
|
||||
/* bottom right */
|
||||
ex, ey, width, height,
|
||||
tx2, ty2,
|
||||
1.0, 1.0
|
||||
};
|
||||
|
||||
cogl_rectangles_with_texture_coords (rectangles, 9);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
st_texture_frame_set_frame_internal (StTextureFrame *frame,
|
||||
gfloat top,
|
||||
gfloat right,
|
||||
gfloat bottom,
|
||||
gfloat left)
|
||||
{
|
||||
StTextureFramePrivate *priv = frame->priv;
|
||||
GObject *gobject = G_OBJECT (frame);
|
||||
gboolean changed = FALSE;
|
||||
|
||||
g_object_freeze_notify (gobject);
|
||||
|
||||
if (priv->top != top)
|
||||
{
|
||||
priv->top = top;
|
||||
g_object_notify (gobject, "top");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->right != right)
|
||||
{
|
||||
priv->right = right;
|
||||
g_object_notify (gobject, "right");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->bottom != bottom)
|
||||
{
|
||||
priv->bottom = bottom;
|
||||
g_object_notify (gobject, "bottom");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->left != left)
|
||||
{
|
||||
priv->left = left;
|
||||
g_object_notify (gobject, "left");
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
|
||||
|
||||
g_object_thaw_notify (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTextureFrame *frame = ST_TEXTURE_FRAME (gobject);
|
||||
StTextureFramePrivate *priv = frame->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT_TEXTURE:
|
||||
st_texture_frame_set_parent_texture (frame,
|
||||
g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TOP:
|
||||
st_texture_frame_set_frame_internal (frame,
|
||||
g_value_get_float (value),
|
||||
priv->right,
|
||||
priv->bottom,
|
||||
priv->left);
|
||||
break;
|
||||
|
||||
case PROP_RIGHT:
|
||||
st_texture_frame_set_frame_internal (frame,
|
||||
priv->top,
|
||||
g_value_get_float (value),
|
||||
priv->bottom,
|
||||
priv->left);
|
||||
break;
|
||||
|
||||
case PROP_BOTTOM:
|
||||
st_texture_frame_set_frame_internal (frame,
|
||||
priv->top,
|
||||
priv->right,
|
||||
g_value_get_float (value),
|
||||
priv->left);
|
||||
break;
|
||||
|
||||
case PROP_LEFT:
|
||||
st_texture_frame_set_frame_internal (frame,
|
||||
priv->top,
|
||||
priv->right,
|
||||
priv->bottom,
|
||||
g_value_get_float (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT_TEXTURE:
|
||||
g_value_set_object (value, priv->parent_texture);
|
||||
break;
|
||||
|
||||
case PROP_LEFT:
|
||||
g_value_set_float (value, priv->left);
|
||||
break;
|
||||
|
||||
case PROP_TOP:
|
||||
g_value_set_float (value, priv->top);
|
||||
break;
|
||||
|
||||
case PROP_RIGHT:
|
||||
g_value_set_float (value, priv->right);
|
||||
break;
|
||||
|
||||
case PROP_BOTTOM:
|
||||
g_value_set_float (value, priv->bottom);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_dispose (GObject *gobject)
|
||||
{
|
||||
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
|
||||
|
||||
if (priv->parent_texture)
|
||||
{
|
||||
g_object_unref (priv->parent_texture);
|
||||
priv->parent_texture = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_texture_frame_parent_class)->dispose (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_class_init (StTextureFrameClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (StTextureFramePrivate));
|
||||
|
||||
actor_class->get_preferred_width =
|
||||
st_texture_frame_get_preferred_width;
|
||||
actor_class->get_preferred_height =
|
||||
st_texture_frame_get_preferred_height;
|
||||
actor_class->paint = st_texture_frame_paint;
|
||||
|
||||
gobject_class->set_property = st_texture_frame_set_property;
|
||||
gobject_class->get_property = st_texture_frame_get_property;
|
||||
gobject_class->dispose = st_texture_frame_dispose;
|
||||
|
||||
pspec = g_param_spec_object ("parent-texture",
|
||||
"Parent Texture",
|
||||
"The parent ClutterTexture",
|
||||
CLUTTER_TYPE_TEXTURE,
|
||||
ST_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT);
|
||||
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
|
||||
|
||||
pspec = g_param_spec_float ("left",
|
||||
"Left",
|
||||
"Left offset",
|
||||
0, G_MAXFLOAT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
|
||||
|
||||
pspec = g_param_spec_float ("top",
|
||||
"Top",
|
||||
"Top offset",
|
||||
0, G_MAXFLOAT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
|
||||
|
||||
pspec = g_param_spec_float ("bottom",
|
||||
"Bottom",
|
||||
"Bottom offset",
|
||||
0, G_MAXFLOAT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
|
||||
|
||||
pspec = g_param_spec_float ("right",
|
||||
"Right",
|
||||
"Right offset",
|
||||
0, G_MAXFLOAT,
|
||||
0,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_texture_frame_init (StTextureFrame *self)
|
||||
{
|
||||
StTextureFramePrivate *priv;
|
||||
|
||||
self->priv = priv = ST_TEXTURE_FRAME_GET_PRIVATE (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_frame_new:
|
||||
* @texture: a #ClutterTexture or %NULL
|
||||
* @left: left margin preserving its content
|
||||
* @top: top margin preserving its content
|
||||
* @right: right margin preserving its content
|
||||
* @bottom: bottom margin preserving its content
|
||||
*
|
||||
* A #StTextureFrame is a specialized texture that efficiently clones
|
||||
* an area of the given @texture while keeping preserving portions of the
|
||||
* same texture.
|
||||
*
|
||||
* A #StTextureFrame can be used to make a rectangular texture fit a
|
||||
* given size without stretching its borders.
|
||||
*
|
||||
* Return value: the newly created #StTextureFrame
|
||||
*/
|
||||
ClutterActor*
|
||||
st_texture_frame_new (ClutterTexture *texture,
|
||||
gfloat top,
|
||||
gfloat right,
|
||||
gfloat bottom,
|
||||
gfloat left)
|
||||
{
|
||||
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
|
||||
|
||||
return g_object_new (ST_TYPE_TEXTURE_FRAME,
|
||||
"parent-texture", texture,
|
||||
"top", top,
|
||||
"right", right,
|
||||
"bottom", bottom,
|
||||
"left", left,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_frame_get_parent_texture:
|
||||
* @frame: A #StTextureFrame
|
||||
*
|
||||
* Return the texture used by the #StTextureFrame
|
||||
*
|
||||
* Returns: (transfer none): a #ClutterTexture owned by the #StTextureFrame
|
||||
*/
|
||||
ClutterTexture *
|
||||
st_texture_frame_get_parent_texture (StTextureFrame *frame)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_TEXTURE_FRAME (frame), NULL);
|
||||
|
||||
return frame->priv->parent_texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_frame_set_parent_texture:
|
||||
* @frame: A #StTextureFrame
|
||||
* @texture: A #ClutterTexture
|
||||
*
|
||||
* Set the #ClutterTexture used by this #StTextureFrame
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_texture_frame_set_parent_texture (StTextureFrame *frame,
|
||||
ClutterTexture *texture)
|
||||
{
|
||||
StTextureFramePrivate *priv;
|
||||
gboolean was_visible;
|
||||
|
||||
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
|
||||
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
|
||||
|
||||
priv = frame->priv;
|
||||
|
||||
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
|
||||
|
||||
if (priv->parent_texture == texture)
|
||||
return;
|
||||
|
||||
if (priv->parent_texture)
|
||||
{
|
||||
g_object_unref (priv->parent_texture);
|
||||
priv->parent_texture = NULL;
|
||||
|
||||
if (was_visible)
|
||||
clutter_actor_hide (CLUTTER_ACTOR (frame));
|
||||
}
|
||||
|
||||
if (texture)
|
||||
{
|
||||
priv->parent_texture = g_object_ref_sink (texture);
|
||||
|
||||
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
|
||||
clutter_actor_show (CLUTTER_ACTOR (frame));
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
|
||||
|
||||
g_object_notify (G_OBJECT (frame), "parent-texture");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_frame_set_frame:
|
||||
* @frame: A #StTextureFrame
|
||||
* @top: width of the top slice
|
||||
* @right: width of the right slice
|
||||
* @bottom: width of the bottom slice
|
||||
* @left: width of the left slice
|
||||
*
|
||||
* Set the slice lines of the specified frame. The slices are calculated as
|
||||
* widths from the edge of the frame.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_texture_frame_set_frame (StTextureFrame *frame,
|
||||
gfloat top,
|
||||
gfloat right,
|
||||
gfloat bottom,
|
||||
gfloat left)
|
||||
{
|
||||
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
|
||||
|
||||
st_texture_frame_set_frame_internal (frame, top, right, bottom, left);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_frame_get_frame:
|
||||
* @frame: A #StTextureFrame
|
||||
* @top: width of the top slice
|
||||
* @right: width of the right slice
|
||||
* @bottom: width of the bottom slice
|
||||
* @left: width of the left slice
|
||||
*
|
||||
* Retrieve the current slice lines from the specified frame.
|
||||
*
|
||||
*/
|
||||
void
|
||||
st_texture_frame_get_frame (StTextureFrame *frame,
|
||||
gfloat *top,
|
||||
gfloat *right,
|
||||
gfloat *bottom,
|
||||
gfloat *left)
|
||||
{
|
||||
StTextureFramePrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
|
||||
|
||||
priv = frame->priv;
|
||||
|
||||
if (top)
|
||||
*top = priv->top;
|
||||
|
||||
if (right)
|
||||
*right = priv->right;
|
||||
|
||||
if (bottom)
|
||||
*bottom = priv->bottom;
|
||||
|
||||
if (left)
|
||||
*left = priv->left;
|
||||
}
|
94
src/st/st-texture-frame.h
Normal file
94
src/st/st-texture-frame.h
Normal file
@ -0,0 +1,94 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-texture-frame.h: Expandible texture actor
|
||||
*
|
||||
* Copyright 2007, 2008 OpenedHand Ltd
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_TEXTURE_FRAME_H__
|
||||
#define __ST_TEXTURE_FRAME_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_TEXTURE_FRAME (st_texture_frame_get_type ())
|
||||
#define ST_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrame))
|
||||
#define ST_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
|
||||
#define ST_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_FRAME))
|
||||
#define ST_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_FRAME))
|
||||
#define ST_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
|
||||
|
||||
typedef struct _StTextureFrame StTextureFrame;
|
||||
typedef struct _StTextureFramePrivate StTextureFramePrivate;
|
||||
typedef struct _StTextureFrameClass StTextureFrameClass;
|
||||
|
||||
/**
|
||||
* StTextureFrame:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StTextureFrame
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterActor parent_instance;
|
||||
|
||||
StTextureFramePrivate *priv;
|
||||
};
|
||||
|
||||
struct _StTextureFrameClass
|
||||
{
|
||||
ClutterActorClass parent_class;
|
||||
|
||||
/* padding for future expansion */
|
||||
void (*_clutter_box_1) (void);
|
||||
void (*_clutter_box_2) (void);
|
||||
void (*_clutter_box_3) (void);
|
||||
void (*_clutter_box_4) (void);
|
||||
};
|
||||
|
||||
GType st_texture_frame_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor * st_texture_frame_new (ClutterTexture *texture,
|
||||
gfloat top,
|
||||
gfloat right,
|
||||
gfloat bottom,
|
||||
gfloat left);
|
||||
void st_texture_frame_set_parent_texture (StTextureFrame *frame,
|
||||
ClutterTexture *texture);
|
||||
ClutterTexture *st_texture_frame_get_parent_texture (StTextureFrame *frame);
|
||||
void st_texture_frame_set_frame (StTextureFrame *frame,
|
||||
gfloat top,
|
||||
gfloat right,
|
||||
gfloat bottom,
|
||||
gfloat left);
|
||||
void st_texture_frame_get_frame (StTextureFrame *frame,
|
||||
gfloat *top,
|
||||
gfloat *right,
|
||||
gfloat *bottom,
|
||||
gfloat *left);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TEXTURE_FRAME_H__ */
|
287
src/st/st-theme-context.c
Normal file
287
src/st/st-theme-context.c
Normal file
@ -0,0 +1,287 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "st-theme.h"
|
||||
#include "st-theme-context.h"
|
||||
|
||||
struct _StThemeContext {
|
||||
GObject parent;
|
||||
|
||||
double resolution;
|
||||
PangoFontDescription *font;
|
||||
StThemeNode *root_node;
|
||||
StTheme *theme;
|
||||
};
|
||||
|
||||
struct _StThemeContextClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
#define DEFAULT_RESOLUTION 96.
|
||||
#define DEFAULT_FONT "sans-serif 10"
|
||||
|
||||
enum
|
||||
{
|
||||
CHANGED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
st_theme_context_finalize (GObject *object)
|
||||
{
|
||||
StThemeContext *context = ST_THEME_CONTEXT (object);
|
||||
|
||||
if (context->root_node)
|
||||
g_object_unref (context->root_node);
|
||||
if (context->theme)
|
||||
g_object_unref (context->theme);
|
||||
|
||||
pango_font_description_free (context->font);
|
||||
|
||||
G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_context_class_init (StThemeContextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = st_theme_context_finalize;
|
||||
|
||||
signals[CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, /* no default handler slot */
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_context_init (StThemeContext *context)
|
||||
{
|
||||
context->resolution = DEFAULT_RESOLUTION;
|
||||
context->font = pango_font_description_from_string (DEFAULT_FONT);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_new:
|
||||
*
|
||||
* Create a new theme context not associated with any #ClutterStage.
|
||||
* This can be useful in testing scenarios, or if using StThemeContext
|
||||
* with something other than #ClutterActor objects, but you generally
|
||||
* should use st_theme_context_get_for_stage() instead.
|
||||
*/
|
||||
StThemeContext *
|
||||
st_theme_context_new (void)
|
||||
{
|
||||
StThemeContext *context;
|
||||
|
||||
context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static void
|
||||
on_stage_destroy (ClutterStage *stage)
|
||||
{
|
||||
StThemeContext *context = st_theme_context_get_for_stage (stage);
|
||||
|
||||
g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL);
|
||||
g_object_unref (context);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_context_changed (StThemeContext *context)
|
||||
{
|
||||
StThemeNode *old_root = context->root_node;
|
||||
context->root_node = NULL;
|
||||
|
||||
g_signal_emit (context, signals[CHANGED], 0);
|
||||
|
||||
if (old_root)
|
||||
g_object_unref (old_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_for_stage:
|
||||
* @stage: a #ClutterStage
|
||||
*
|
||||
* Gets a singleton theme context associated with the stage.
|
||||
*
|
||||
* Return value: (transfer none): the singleton theme context for the stage
|
||||
*/
|
||||
StThemeContext *
|
||||
st_theme_context_get_for_stage (ClutterStage *stage)
|
||||
{
|
||||
StThemeContext *context;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
|
||||
|
||||
context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
|
||||
if (context)
|
||||
return context;
|
||||
|
||||
context = st_theme_context_new ();
|
||||
g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
|
||||
g_signal_connect (stage, "destroy",
|
||||
G_CALLBACK (on_stage_destroy), NULL);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_theme:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Sets the default set of theme stylesheets for the context. This theme will
|
||||
* be used for the root node and for nodes descending from it, unless some other
|
||||
* style is explicitely specified.
|
||||
*/
|
||||
void
|
||||
st_theme_context_set_theme (StThemeContext *context,
|
||||
StTheme *theme)
|
||||
{
|
||||
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
||||
g_return_if_fail (theme == NULL || ST_IS_THEME (theme));
|
||||
|
||||
if (context->theme != theme)
|
||||
{
|
||||
if (context->theme)
|
||||
g_object_unref (context->theme);
|
||||
|
||||
context->theme = theme;
|
||||
|
||||
if (context->theme)
|
||||
g_object_ref (context->theme);
|
||||
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_theme:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the default theme for the context. See st_theme_context_set_theme()
|
||||
*
|
||||
* Return value: (transfer none): the default theme for the context
|
||||
*/
|
||||
StTheme *
|
||||
st_theme_context_get_theme (StThemeContext *context)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
|
||||
|
||||
return context->theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_resolution:
|
||||
* @context: a #StThemeContext
|
||||
* @resolution: resolution of the context (number of pixels in an "inch")
|
||||
*
|
||||
* Sets the resolution of the theme context. This is the scale factor
|
||||
* used to convert between points and the length units pt, in, and cm.
|
||||
* This does not necessarily need to correspond to the actual number
|
||||
* resolution of the device. A value of 72. means that points and
|
||||
* pixels are identical. The default value is 96.
|
||||
*/
|
||||
void
|
||||
st_theme_context_set_resolution (StThemeContext *context,
|
||||
double resolution)
|
||||
{
|
||||
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
||||
|
||||
if (resolution == context->resolution)
|
||||
return;
|
||||
|
||||
context->resolution = resolution;
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_resolution:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the current resolution of the theme context.
|
||||
* See st_theme_context_set_resolution().
|
||||
*
|
||||
* Return value: the resolution (in dots-per-"inch")
|
||||
*/
|
||||
double
|
||||
st_theme_context_get_resolution (StThemeContext *context)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), DEFAULT_RESOLUTION);
|
||||
|
||||
return context->resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_font:
|
||||
* @context: a #StThemeContext
|
||||
* @font: the default font for theme context
|
||||
*
|
||||
* Sets the default font for the theme context. This is the font that
|
||||
* is inherited by the root node of the tree of theme nodes. If the
|
||||
* font is not overriden, then this font will be used. If the font is
|
||||
* partially modified (for example, with 'font-size: 110%', then that
|
||||
* modification is based on this font.
|
||||
*/
|
||||
void
|
||||
st_theme_context_set_font (StThemeContext *context,
|
||||
const PangoFontDescription *font)
|
||||
{
|
||||
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
||||
g_return_if_fail (font != NULL);
|
||||
|
||||
if (context->font == font ||
|
||||
pango_font_description_equal (context->font, font))
|
||||
return;
|
||||
|
||||
pango_font_description_free (context->font);
|
||||
context->font = pango_font_description_copy (font);
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_font:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the default font for the theme context. See st_theme_context_set_font().
|
||||
*
|
||||
* Return value: the default font for the theme context.
|
||||
*/
|
||||
const PangoFontDescription *
|
||||
st_theme_context_get_font (StThemeContext *context)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
|
||||
|
||||
return context->font;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_root_node:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the root node of the tree of theme style nodes that associated with this
|
||||
* context. For the node tree associated with a stage, this node represents
|
||||
* styles applied to the stage itself.
|
||||
*
|
||||
* Return value: (transfer none): the root node of the context's style tree
|
||||
*/
|
||||
StThemeNode *
|
||||
st_theme_context_get_root_node (StThemeContext *context)
|
||||
{
|
||||
if (context->root_node == NULL)
|
||||
context->root_node = st_theme_node_new (context, NULL, context->theme,
|
||||
G_TYPE_NONE, NULL, NULL, NULL, NULL);
|
||||
|
||||
return context->root_node;
|
||||
}
|
50
src/st/st-theme-context.h
Normal file
50
src/st/st-theme-context.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_THEME_CONTEXT_H__
|
||||
#define __ST_THEME_CONTEXT_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <pango/pango.h>
|
||||
#include "st-theme-node.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StThemeContext
|
||||
* @short_description: holds global information about a tree of styled objects
|
||||
*
|
||||
* #StThemeContext is responsible for managing information global to a tree of styled objects,
|
||||
* such as the set of stylesheets or the default font. In normal usage, a #StThemeContext
|
||||
* is bound to a #ClutterStage; a singleton #StThemeContext can be obtained for a #ClutterStage
|
||||
* by using st_theme_context_get_for_stage().
|
||||
*/
|
||||
|
||||
typedef struct _StThemeContextClass StThemeContextClass;
|
||||
|
||||
#define ST_TYPE_THEME_CONTEXT (st_theme_context_get_type ())
|
||||
#define ST_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_CONTEXT, StThemeContext))
|
||||
#define ST_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_CONTEXT, StThemeContextClass))
|
||||
#define ST_IS_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_CONTEXT))
|
||||
#define ST_IS_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_CONTEXT))
|
||||
#define ST_THEME_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_CONTEXT, StThemeContextClass))
|
||||
|
||||
GType st_theme_context_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StThemeContext *st_theme_context_new (void);
|
||||
StThemeContext *st_theme_context_get_for_stage (ClutterStage *stage);
|
||||
|
||||
void st_theme_context_set_theme (StThemeContext *context,
|
||||
StTheme *theme);
|
||||
StTheme * st_theme_context_get_theme (StThemeContext *context);
|
||||
|
||||
void st_theme_context_set_resolution (StThemeContext *context,
|
||||
gdouble resolution);
|
||||
double st_theme_context_get_resolution (StThemeContext *context);
|
||||
void st_theme_context_set_font (StThemeContext *context,
|
||||
const PangoFontDescription *font);
|
||||
const PangoFontDescription *st_theme_context_get_font (StThemeContext *context);
|
||||
|
||||
StThemeNode * st_theme_context_get_root_node (StThemeContext *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_CONTEXT_H__ */
|
2146
src/st/st-theme-node.c
Normal file
2146
src/st/st-theme-node.c
Normal file
File diff suppressed because it is too large
Load Diff
153
src/st/st-theme-node.h
Normal file
153
src/st/st-theme-node.h
Normal file
@ -0,0 +1,153 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_THEME_NODE_H__
|
||||
#define __ST_THEME_NODE_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include "st-border-image.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StThemeNode
|
||||
* @short_description: style information for one node in a tree of themed objects
|
||||
*
|
||||
* A #StThemeNode represents the CSS style information (the set of CSS properties) for one
|
||||
* node in a tree of themed objects. In typical usage, it represents the style information
|
||||
* for a single #ClutterActor. A #StThemeNode is immutable: attributes such as the
|
||||
* CSS classes for the node are passed in at construction. If the attributes of the node
|
||||
* or any parent node change, the node should be discarded and a new node created.
|
||||
* #StThemeNode has generic accessors to look up properties by name and specific
|
||||
* accessors for standard CSS properties that add caching and handling of various
|
||||
* details of the CSS specification. #StThemeNode also has convenience functions to help
|
||||
* in implementing a #ClutterActor with borders and padding.
|
||||
*/
|
||||
|
||||
typedef struct _StTheme StTheme;
|
||||
typedef struct _StThemeContext StThemeContext;
|
||||
|
||||
typedef struct _StThemeNode StThemeNode;
|
||||
typedef struct _StThemeNodeClass StThemeNodeClass;
|
||||
|
||||
#define ST_TYPE_THEME_NODE (st_theme_node_get_type ())
|
||||
#define ST_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_NODE, StThemeNode))
|
||||
#define ST_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_NODE, StThemeNodeClass))
|
||||
#define ST_IS_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_NODE))
|
||||
#define ST_IS_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_NODE))
|
||||
#define ST_THEME_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_NODE, StThemeNodeClass))
|
||||
|
||||
typedef enum {
|
||||
ST_SIDE_TOP,
|
||||
ST_SIDE_RIGHT,
|
||||
ST_SIDE_BOTTOM,
|
||||
ST_SIDE_LEFT
|
||||
} StSide;
|
||||
|
||||
typedef enum {
|
||||
ST_CORNER_TOPLEFT,
|
||||
ST_CORNER_TOPRIGHT,
|
||||
ST_CORNER_BOTTOMRIGHT,
|
||||
ST_CORNER_BOTTOMLEFT
|
||||
} StCorner;
|
||||
|
||||
/* These are the CSS values; that doesn't mean we have to implement blink... */
|
||||
typedef enum {
|
||||
ST_TEXT_DECORATION_UNDERLINE = 1 << 0,
|
||||
ST_TEXT_DECORATION_OVERLINE = 1 << 1,
|
||||
ST_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
|
||||
ST_TEXT_DECORATION_BLINK = 1 << 3
|
||||
} StTextDecoration;
|
||||
|
||||
GType st_theme_node_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StThemeNode *st_theme_node_new (StThemeContext *context,
|
||||
StThemeNode *parent_node, /* can be null */
|
||||
StTheme *theme, /* can be null */
|
||||
GType element_type,
|
||||
const char *element_id,
|
||||
const char *element_class,
|
||||
const char *pseudo_class,
|
||||
const char *inline_style);
|
||||
|
||||
StThemeNode *st_theme_node_get_parent (StThemeNode *node);
|
||||
|
||||
StTheme *st_theme_node_get_theme (StThemeNode *node);
|
||||
|
||||
GType st_theme_node_get_element_type (StThemeNode *node);
|
||||
const char *st_theme_node_get_element_id (StThemeNode *node);
|
||||
const char *st_theme_node_get_element_class (StThemeNode *node);
|
||||
const char *st_theme_node_get_pseudo_class (StThemeNode *node);
|
||||
|
||||
/* Generic getters ... these are not cached so are less efficient. The other
|
||||
* reason for adding the more specific version is that we can handle the
|
||||
* details of the actual CSS rules, which can be complicated, especially
|
||||
* for fonts
|
||||
*/
|
||||
gboolean st_theme_node_get_color (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
ClutterColor *color);
|
||||
|
||||
gboolean st_theme_node_get_double (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
double *value);
|
||||
|
||||
gboolean st_theme_node_get_length (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
gdouble *length);
|
||||
|
||||
/* Specific getters for particular properties: cached
|
||||
*/
|
||||
void st_theme_node_get_background_color (StThemeNode *node,
|
||||
ClutterColor *color);
|
||||
void st_theme_node_get_foreground_color (StThemeNode *node,
|
||||
ClutterColor *color);
|
||||
|
||||
const char *st_theme_node_get_background_image (StThemeNode *node);
|
||||
|
||||
double st_theme_node_get_border_width (StThemeNode *node,
|
||||
StSide side);
|
||||
double st_theme_node_get_border_radius (StThemeNode *node,
|
||||
StCorner corner);
|
||||
void st_theme_node_get_border_color (StThemeNode *node,
|
||||
StSide side,
|
||||
ClutterColor *color);
|
||||
|
||||
double st_theme_node_get_padding (StThemeNode *node,
|
||||
StSide side);
|
||||
|
||||
StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node);
|
||||
|
||||
/* Font rule processing is pretty complicated, so we just hardcode it
|
||||
* under the standard font/font-family/font-size/etc names. This means
|
||||
* you can't have multiple separate styled fonts for a single item,
|
||||
* but that should be OK.
|
||||
*/
|
||||
const PangoFontDescription *st_theme_node_get_font (StThemeNode *node);
|
||||
|
||||
StBorderImage *st_theme_node_get_border_image (StThemeNode *node);
|
||||
|
||||
/* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */
|
||||
void st_theme_node_adjust_for_height (StThemeNode *node,
|
||||
float *for_height);
|
||||
void st_theme_node_adjust_preferred_width (StThemeNode *node,
|
||||
float *min_width_p,
|
||||
float *natural_width_p);
|
||||
void st_theme_node_adjust_for_width (StThemeNode *node,
|
||||
float *for_width);
|
||||
void st_theme_node_adjust_preferred_height (StThemeNode *node,
|
||||
float *min_height_p,
|
||||
float *natural_height_p);
|
||||
|
||||
/* Helper for allocate() ClutterActor vfunc */
|
||||
void st_theme_node_get_content_box (StThemeNode *node,
|
||||
const ClutterActorBox *actor_box,
|
||||
ClutterActorBox *content_box);
|
||||
|
||||
gboolean st_theme_node_geometry_equal (StThemeNode *node,
|
||||
StThemeNode *other);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_NODE_H__ */
|
22
src/st/st-theme-private.h
Normal file
22
src/st/st-theme-private.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_THEME_PRIVATE_H__
|
||||
#define __ST_THEME_PRIVATE_H__
|
||||
|
||||
#include <libcroco/libcroco.h>
|
||||
#include "st-theme.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GPtrArray *_st_theme_get_matched_properties (StTheme *theme,
|
||||
StThemeNode *node);
|
||||
|
||||
/* Resolve an URL from the stylesheet to a filename */
|
||||
char *_st_theme_resolve_url (StTheme *theme,
|
||||
CRStyleSheet *base_stylesheet,
|
||||
const char *url);
|
||||
|
||||
CRDeclaration *_st_theme_parse_declaration_list (const char *str);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_PRIVATE_H__ */
|
1062
src/st/st-theme.c
Normal file
1062
src/st/st-theme.c
Normal file
File diff suppressed because it is too large
Load Diff
38
src/st/st-theme.h
Normal file
38
src/st/st-theme.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_THEME_H__
|
||||
#define __ST_THEME_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "st-theme-node.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StTheme
|
||||
* @short_description: a set of stylesheets
|
||||
*
|
||||
* #StTheme holds a set of stylesheets. (The "cascade" of the name
|
||||
* Cascading Stylesheets.) A #StTheme can be set to apply to all the actors
|
||||
* in a stage using st_theme_context_set_theme() or applied to a subtree
|
||||
* of actors using st_widget_set_theme().
|
||||
*/
|
||||
|
||||
typedef struct _StThemeClass StThemeClass;
|
||||
|
||||
#define ST_TYPE_THEME (st_theme_get_type ())
|
||||
#define ST_THEME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME, StTheme))
|
||||
#define ST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME, StThemeClass))
|
||||
#define ST_IS_THEME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME))
|
||||
#define ST_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME))
|
||||
#define ST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME, StThemeClass))
|
||||
|
||||
GType st_theme_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StTheme *st_theme_new (const char *application_stylesheet,
|
||||
const char *theme_stylesheet,
|
||||
const char *default_stylesheet);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_H__ */
|
665
src/st/st-tooltip.c
Normal file
665
src/st/st-tooltip.c
Normal file
@ -0,0 +1,665 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-tooltip.c: Plain tooltip actor
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-tooltip
|
||||
* @short_description: A tooltip widget
|
||||
*
|
||||
* #StTooltip implements a single tooltip. It should not normally be created
|
||||
* by the application but by the widget implementing tooltip capabilities, for
|
||||
* example, #st_button_set_tooltip().
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-tooltip.h"
|
||||
|
||||
#include "st-widget.h"
|
||||
#include "st-private.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_LABEL,
|
||||
PROP_TIP_AREA
|
||||
};
|
||||
|
||||
#define ST_TOOLTIP_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TOOLTIP, StTooltipPrivate))
|
||||
|
||||
struct _StTooltipPrivate
|
||||
{
|
||||
ClutterActor *label;
|
||||
|
||||
gfloat arrow_offset;
|
||||
gboolean actor_below;
|
||||
|
||||
ClutterGeometry *tip_area;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (StTooltip, st_tooltip, ST_TYPE_WIDGET);
|
||||
|
||||
static void
|
||||
st_tooltip_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTooltip *tooltip = ST_TOOLTIP (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
st_tooltip_set_label (tooltip, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_TIP_AREA:
|
||||
st_tooltip_set_tip_area (tooltip, g_value_get_boxed (value));
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (gobject)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
|
||||
break;
|
||||
|
||||
case PROP_TIP_AREA:
|
||||
g_value_set_boxed (value, priv->tip_area);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_style_changed (StWidget *self)
|
||||
{
|
||||
StTooltipPrivate *priv;
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string;
|
||||
|
||||
priv = ST_TOOLTIP (self)->priv;
|
||||
theme_node = st_widget_get_theme_node (self);
|
||||
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (priv->label), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
|
||||
g_free (font_string);
|
||||
|
||||
ST_WIDGET_CLASS (st_tooltip_parent_class)->style_changed (self);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
gfloat min_label_w, natural_label_w;
|
||||
gfloat label_height, arrow_height;
|
||||
ClutterActor *arrow_image;
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
if (arrow_image)
|
||||
{
|
||||
clutter_actor_get_preferred_height (arrow_image,
|
||||
-1,
|
||||
NULL,
|
||||
&arrow_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow_height = 0;
|
||||
}
|
||||
|
||||
if (for_height > -1)
|
||||
{
|
||||
label_height = for_height - arrow_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
label_height = -1;
|
||||
}
|
||||
|
||||
if (priv->label)
|
||||
{
|
||||
clutter_actor_get_preferred_width (priv->label,
|
||||
label_height,
|
||||
&min_label_w,
|
||||
&natural_label_w);
|
||||
}
|
||||
else
|
||||
{
|
||||
min_label_w = 0;
|
||||
natural_label_w = 0;
|
||||
}
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_get_preferred_height (ClutterActor *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
gfloat arrow_height;
|
||||
gfloat min_label_h, natural_label_h;
|
||||
ClutterActor *arrow_image;
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
|
||||
if (arrow_image && !priv->actor_below)
|
||||
{
|
||||
clutter_actor_get_preferred_height (arrow_image,
|
||||
-1,
|
||||
NULL,
|
||||
&arrow_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow_height = 0;
|
||||
}
|
||||
|
||||
if (priv->label)
|
||||
{
|
||||
clutter_actor_get_preferred_height (priv->label,
|
||||
for_width,
|
||||
&min_label_h,
|
||||
&natural_label_h);
|
||||
}
|
||||
else
|
||||
{
|
||||
min_label_h = 0;
|
||||
natural_label_h = 0;
|
||||
}
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = arrow_height + min_label_h;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = arrow_height + natural_label_h;
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
|
||||
ClutterActorBox content_box, child_box, arrow_box;
|
||||
gfloat arrow_height, arrow_width;
|
||||
ClutterActor *border_image, *arrow_image;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->allocate (self,
|
||||
box,
|
||||
flags);
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &content_box);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
|
||||
if (arrow_image && !priv->actor_below)
|
||||
{
|
||||
clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height);
|
||||
clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width);
|
||||
|
||||
arrow_box.x1 = (float)(priv->arrow_offset) - (int)(arrow_width / 2);
|
||||
arrow_box.y1 = 0;
|
||||
arrow_box.x2 = arrow_box.x1 + arrow_width;
|
||||
arrow_box.y2 = arrow_box.y1 + arrow_height;
|
||||
|
||||
clutter_actor_allocate (arrow_image, &arrow_box, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow_height = 0;
|
||||
arrow_width = 0;
|
||||
}
|
||||
|
||||
child_box.x1 = child_box.y1 = 0;
|
||||
child_box.x2 = (box->x2 - box->x1);
|
||||
child_box.y2 = (box->y2 - box->y1);
|
||||
|
||||
/* remove the space that is used by the arrow */
|
||||
child_box.y1 += arrow_height;
|
||||
|
||||
border_image = st_widget_get_border_image (ST_WIDGET (self));
|
||||
if (border_image)
|
||||
clutter_actor_allocate (border_image, &child_box, flags);
|
||||
|
||||
if (priv->label)
|
||||
{
|
||||
child_box = content_box;
|
||||
child_box.y1 += arrow_height;
|
||||
|
||||
clutter_actor_allocate (priv->label, &child_box, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
st_tooltip_paint (ClutterActor *self)
|
||||
{
|
||||
ClutterActor *border_image, *arrow_image;
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
|
||||
border_image = st_widget_get_border_image (ST_WIDGET (self));
|
||||
if (border_image)
|
||||
clutter_actor_paint (border_image);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
if (arrow_image && !priv->actor_below)
|
||||
clutter_actor_paint (arrow_image);
|
||||
|
||||
clutter_actor_paint (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_map (ClutterActor *self)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
ClutterActor *border_image, *arrow_image;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->map (self);
|
||||
|
||||
border_image = st_widget_get_border_image (ST_WIDGET (self));
|
||||
if (border_image)
|
||||
clutter_actor_map (border_image);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
if (arrow_image)
|
||||
clutter_actor_map (arrow_image);
|
||||
|
||||
clutter_actor_map (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_unmap (ClutterActor *self)
|
||||
{
|
||||
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
|
||||
ClutterActor *border_image, *arrow_image;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->unmap (self);
|
||||
|
||||
border_image = st_widget_get_border_image (ST_WIDGET (self));
|
||||
if (border_image)
|
||||
clutter_actor_unmap (border_image);
|
||||
|
||||
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
|
||||
if (arrow_image)
|
||||
clutter_actor_unmap (arrow_image);
|
||||
|
||||
clutter_actor_unmap (priv->label);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_class_init (StTooltipClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StTooltipPrivate));
|
||||
|
||||
gobject_class->set_property = st_tooltip_set_property;
|
||||
gobject_class->get_property = st_tooltip_get_property;
|
||||
|
||||
actor_class->get_preferred_width = st_tooltip_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_tooltip_get_preferred_height;
|
||||
actor_class->allocate = st_tooltip_allocate;
|
||||
actor_class->paint = st_tooltip_paint;
|
||||
actor_class->map = st_tooltip_map;
|
||||
actor_class->unmap = st_tooltip_unmap;
|
||||
|
||||
widget_class->style_changed = st_tooltip_style_changed;
|
||||
|
||||
pspec = g_param_spec_string ("label",
|
||||
"Label",
|
||||
"Label of the tooltip",
|
||||
NULL, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
|
||||
|
||||
pspec = g_param_spec_boxed ("tip-area",
|
||||
"Tip Area",
|
||||
"Area on the stage the tooltip applies to",
|
||||
CLUTTER_TYPE_GEOMETRY,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_init (StTooltip *tooltip)
|
||||
{
|
||||
tooltip->priv = ST_TOOLTIP_GET_PRIVATE (tooltip);
|
||||
|
||||
tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
|
||||
"line-alignment", PANGO_ALIGN_CENTER,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
"use-markup", TRUE,
|
||||
NULL);
|
||||
|
||||
tooltip->priv->tip_area = NULL;
|
||||
|
||||
clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label),
|
||||
CLUTTER_ACTOR (tooltip));
|
||||
|
||||
g_object_set (tooltip, "show-on-set-parent", FALSE, NULL);
|
||||
|
||||
clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_update_position (StTooltip *tooltip)
|
||||
{
|
||||
StTooltipPrivate *priv = tooltip->priv;
|
||||
ClutterGeometry *tip_area = tooltip->priv->tip_area;
|
||||
gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
|
||||
gfloat stage_w, stage_h;
|
||||
ClutterActor *stage;
|
||||
|
||||
/* ensure the tooltip with is not fixed size */
|
||||
clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);
|
||||
|
||||
/* if no area set, just position ourselves top left */
|
||||
if (!priv->tip_area)
|
||||
{
|
||||
clutter_actor_set_position ((ClutterActor*) tooltip, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we need to have a style in case there are padding/border values to take into
|
||||
* account when calculating width/height */
|
||||
st_widget_ensure_style ((StWidget *) tooltip);
|
||||
|
||||
/* find out the tooltip's size */
|
||||
clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);
|
||||
|
||||
/* attempt to place the tooltip */
|
||||
tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
|
||||
tooltip_y = (int)(tip_area->y + tip_area->height);
|
||||
|
||||
stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
|
||||
if (!stage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
clutter_actor_get_size (stage, &stage_w, &stage_h);
|
||||
|
||||
/* make sure the tooltip is not off screen vertically */
|
||||
if (tooltip_w > stage_w)
|
||||
{
|
||||
tooltip_x = 0;
|
||||
clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
|
||||
}
|
||||
else if (tooltip_x < 0)
|
||||
{
|
||||
tooltip_x = 0;
|
||||
}
|
||||
else if (tooltip_x + tooltip_w > stage_w)
|
||||
{
|
||||
tooltip_x = (int)(stage_w) - tooltip_w;
|
||||
}
|
||||
|
||||
/* make sure the tooltip is not off screen horizontally */
|
||||
if (tooltip_y + tooltip_h > stage_h)
|
||||
{
|
||||
priv->actor_below = TRUE;
|
||||
|
||||
/* re-query size as may have changed */
|
||||
clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
|
||||
-1, NULL, &tooltip_h);
|
||||
tooltip_y = tip_area->y - tooltip_h;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->actor_below = FALSE;
|
||||
}
|
||||
|
||||
/* calculate the arrow offset */
|
||||
priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
|
||||
|
||||
clutter_actor_set_position ((ClutterActor*) tooltip, tooltip_x, tooltip_y);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_get_label:
|
||||
* @tooltip: a #StTooltip
|
||||
*
|
||||
* Get the text displayed on the tooltip
|
||||
*
|
||||
* Returns: the text for the tooltip. This must not be freed by the application
|
||||
*/
|
||||
G_CONST_RETURN gchar *
|
||||
st_tooltip_get_label (StTooltip *tooltip)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
|
||||
|
||||
return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_set_label:
|
||||
* @tooltip: a #StTooltip
|
||||
* @text: text to set the label to
|
||||
*
|
||||
* Sets the text displayed on the tooltip
|
||||
*/
|
||||
void
|
||||
st_tooltip_set_label (StTooltip *tooltip,
|
||||
const gchar *text)
|
||||
{
|
||||
StTooltipPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
|
||||
|
||||
priv = tooltip->priv;
|
||||
|
||||
clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
|
||||
|
||||
g_object_notify (G_OBJECT (tooltip), "label");
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_show:
|
||||
* @tooltip: a #StTooltip
|
||||
*
|
||||
* Show the tooltip relative to the associated widget.
|
||||
*/
|
||||
void
|
||||
st_tooltip_show (StTooltip *tooltip)
|
||||
{
|
||||
StTooltipPrivate *priv;
|
||||
ClutterActor *parent;
|
||||
ClutterActor *stage;
|
||||
ClutterActor *self = CLUTTER_ACTOR (tooltip);
|
||||
ClutterAnimation *animation;
|
||||
|
||||
/* make sure we're not currently already animating (e.g. hiding) */
|
||||
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
|
||||
if (animation)
|
||||
clutter_animation_completed (animation);
|
||||
|
||||
priv = tooltip->priv;
|
||||
parent = clutter_actor_get_parent (self);
|
||||
stage = clutter_actor_get_stage (self);
|
||||
|
||||
if (!stage)
|
||||
{
|
||||
g_warning ("StTooltip is not on any stage.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* make sure we're parented on the stage */
|
||||
if (G_UNLIKELY (parent != stage))
|
||||
{
|
||||
g_object_ref (self);
|
||||
clutter_actor_unparent (self);
|
||||
clutter_actor_set_parent (self, stage);
|
||||
g_object_unref (self);
|
||||
parent = stage;
|
||||
}
|
||||
|
||||
/* raise the tooltip to the top */
|
||||
clutter_container_raise_child (CLUTTER_CONTAINER (stage),
|
||||
CLUTTER_ACTOR (tooltip),
|
||||
NULL);
|
||||
|
||||
st_tooltip_update_position (tooltip);
|
||||
|
||||
/* finally show the tooltip... */
|
||||
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->show (self);
|
||||
|
||||
/* and give it some bounce! */
|
||||
g_object_set (G_OBJECT (self),
|
||||
"scale-center-x", priv->arrow_offset,
|
||||
"scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0,
|
||||
NULL);
|
||||
clutter_actor_set_scale (self, 0.0, 0.0);
|
||||
clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC,
|
||||
500,
|
||||
"scale-x", 1.0,
|
||||
"scale-y", 1.0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
st_tooltip_hide_complete (ClutterAnimation *animation,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->hide (actor);
|
||||
g_signal_handlers_disconnect_by_func (actor,
|
||||
st_tooltip_hide_complete,
|
||||
actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_hide:
|
||||
* @tooltip: a #StTooltip
|
||||
*
|
||||
* Hide the tooltip
|
||||
*/
|
||||
void
|
||||
st_tooltip_hide (StTooltip *tooltip)
|
||||
{
|
||||
ClutterAnimation *animation;
|
||||
|
||||
g_return_if_fail (ST_TOOLTIP (tooltip));
|
||||
|
||||
/* make sure we're not currently already animating (e.g. hiding) */
|
||||
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
|
||||
if (animation)
|
||||
clutter_animation_completed (animation);
|
||||
|
||||
g_object_set (G_OBJECT (tooltip),
|
||||
"scale-center-x", tooltip->priv->arrow_offset,
|
||||
NULL);
|
||||
animation =
|
||||
clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE,
|
||||
150,
|
||||
"scale-x", 0.0,
|
||||
"scale-y", 0.0,
|
||||
NULL);
|
||||
g_signal_connect (animation, "completed",
|
||||
G_CALLBACK (st_tooltip_hide_complete), tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_set_tip_area:
|
||||
* @tooltip: A #StTooltip
|
||||
* @area: A #ClutterGeometry
|
||||
*
|
||||
* Set the area on the stage that the tooltip applies to.
|
||||
*/
|
||||
void
|
||||
st_tooltip_set_tip_area (StTooltip *tooltip,
|
||||
const ClutterGeometry *area)
|
||||
{
|
||||
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
|
||||
|
||||
if (tooltip->priv->tip_area)
|
||||
g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area);
|
||||
tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area);
|
||||
|
||||
st_tooltip_update_position (tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_tooltip_get_tip_area:
|
||||
* @tooltip: A #StTooltip
|
||||
*
|
||||
* Retrieve the area on the stage that the tooltip currently applies to
|
||||
*
|
||||
* Returns: the #ClutterGeometry, owned by the tooltip which must not be freed
|
||||
* by the application.
|
||||
*/
|
||||
G_CONST_RETURN ClutterGeometry*
|
||||
st_tooltip_get_tip_area (StTooltip *tooltip)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
|
||||
|
||||
return tooltip->priv->tip_area;
|
||||
}
|
81
src/st/st-tooltip.h
Normal file
81
src/st/st-tooltip.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-tooltip.h: Plain tooltip actor
|
||||
*
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Written by: Thomas Wood <thomas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_TOOLTIP_H__
|
||||
#define __ST_TOOLTIP_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <st/st-bin.h>
|
||||
|
||||
#define ST_TYPE_TOOLTIP (st_tooltip_get_type ())
|
||||
#define ST_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TOOLTIP, StTooltip))
|
||||
#define ST_IS_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TOOLTIP))
|
||||
#define ST_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TOOLTIP, StTooltipClass))
|
||||
#define ST_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TOOLTIP))
|
||||
#define ST_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TOOLTIP, StTooltipClass))
|
||||
|
||||
typedef struct _StTooltip StTooltip;
|
||||
typedef struct _StTooltipPrivate StTooltipPrivate;
|
||||
typedef struct _StTooltipClass StTooltipClass;
|
||||
|
||||
/**
|
||||
* StTooltip:
|
||||
*
|
||||
* The contents of this structure is private and should only be accessed using
|
||||
* the provided API.
|
||||
*/
|
||||
struct _StTooltip
|
||||
{
|
||||
/*< private >*/
|
||||
StBin parent_instance;
|
||||
|
||||
StTooltipPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StTooltipClass
|
||||
{
|
||||
StBinClass parent_class;
|
||||
};
|
||||
|
||||
GType st_tooltip_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_CONST_RETURN gchar *st_tooltip_get_label (StTooltip *tooltip);
|
||||
void st_tooltip_set_label (StTooltip *tooltip,
|
||||
const gchar *text);
|
||||
void st_tooltip_show (StTooltip *tooltip);
|
||||
void st_tooltip_hide (StTooltip *tooltip);
|
||||
|
||||
void st_tooltip_set_tip_area (StTooltip *tooltip,
|
||||
const ClutterGeometry *area);
|
||||
G_CONST_RETURN ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TOOLTIP_H__ */
|
48
src/st/st-types.h
Normal file
48
src/st/st-types.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* Copyright 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-types
|
||||
* @short_description: type definitions used throughout St
|
||||
*
|
||||
* Common types for StWidgets.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_TYPES_H__
|
||||
#define __ST_TYPES_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
ST_ALIGN_START,
|
||||
ST_ALIGN_MIDDLE,
|
||||
ST_ALIGN_END
|
||||
} StAlign;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_TYPES_H__ */
|
1395
src/st/st-widget.c
Normal file
1395
src/st/st-widget.c
Normal file
File diff suppressed because it is too large
Load Diff
117
src/st/st-widget.h
Normal file
117
src/st/st-widget.h
Normal file
@ -0,0 +1,117 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-widget.h: Base class for St actors
|
||||
*
|
||||
* Copyright 2007 OpenedHand
|
||||
* Copyright 2008, 2009 Intel Corporation.
|
||||
*
|
||||
* 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, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef __ST_WIDGET_H__
|
||||
#define __ST_WIDGET_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <st/st-types.h>
|
||||
#include <st/st-theme.h>
|
||||
#include <st/st-theme-node.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_WIDGET (st_widget_get_type ())
|
||||
#define ST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_WIDGET, StWidget))
|
||||
#define ST_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_WIDGET))
|
||||
#define ST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_WIDGET, StWidgetClass))
|
||||
#define ST_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_WIDGET))
|
||||
#define ST_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_WIDGET, StWidgetClass))
|
||||
|
||||
typedef struct _StWidget StWidget;
|
||||
typedef struct _StWidgetPrivate StWidgetPrivate;
|
||||
typedef struct _StWidgetClass StWidgetClass;
|
||||
|
||||
/**
|
||||
* StWidget:
|
||||
*
|
||||
* Base class for stylable actors. The contents of the #StWidget
|
||||
* structure are private and should only be accessed through the
|
||||
* public API.
|
||||
*/
|
||||
struct _StWidget
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterActor parent_instance;
|
||||
|
||||
StWidgetPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* StWidgetClass:
|
||||
*
|
||||
* Base class for stylable actors.
|
||||
*/
|
||||
struct _StWidgetClass
|
||||
{
|
||||
/*< private >*/
|
||||
ClutterActorClass parent_class;
|
||||
|
||||
/* vfuncs */
|
||||
void (* draw_background) (StWidget *self);
|
||||
void (* style_changed) (StWidget *self);
|
||||
};
|
||||
|
||||
GType st_widget_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void st_widget_set_style_pseudo_class (StWidget *actor,
|
||||
const gchar *pseudo_class);
|
||||
G_CONST_RETURN gchar *st_widget_get_style_pseudo_class (StWidget *actor);
|
||||
void st_widget_set_style_class_name (StWidget *actor,
|
||||
const gchar *style_class);
|
||||
G_CONST_RETURN gchar *st_widget_get_style_class_name (StWidget *actor);
|
||||
void st_widget_set_style (StWidget *actor,
|
||||
const gchar *style);
|
||||
G_CONST_RETURN gchar *st_widget_get_style (StWidget *actor);
|
||||
void st_widget_set_theme (StWidget *actor,
|
||||
StTheme *theme);
|
||||
StTheme * st_widget_get_theme (StWidget *actor);
|
||||
|
||||
void st_widget_set_has_tooltip (StWidget *widget,
|
||||
gboolean has_tooltip);
|
||||
gboolean st_widget_get_has_tooltip (StWidget *widget);
|
||||
void st_widget_set_tooltip_text (StWidget *widget,
|
||||
const gchar *text);
|
||||
const gchar* st_widget_get_tooltip_text (StWidget *widget);
|
||||
|
||||
void st_widget_show_tooltip (StWidget *widget);
|
||||
void st_widget_hide_tooltip (StWidget *widget);
|
||||
|
||||
void st_widget_ensure_style (StWidget *widget);
|
||||
|
||||
|
||||
/* Only to be used by sub-classes of StWidget */
|
||||
void st_widget_style_changed (StWidget *widget);
|
||||
StThemeNode *st_widget_get_theme_node (StWidget *widget);
|
||||
|
||||
ClutterActor *st_widget_get_background_image (StWidget *actor);
|
||||
ClutterActor *st_widget_get_border_image (StWidget *actor);
|
||||
void st_widget_draw_background (StWidget *widget);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_WIDGET_H__ */
|
392
src/st/test-theme.c
Normal file
392
src/st/test-theme.c
Normal file
@ -0,0 +1,392 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include "st-theme.h"
|
||||
#include "st-theme-context.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
static StThemeNode *root;
|
||||
static StThemeNode *group1;
|
||||
static StThemeNode *text1;
|
||||
static StThemeNode *text2;
|
||||
static StThemeNode *group2;
|
||||
static StThemeNode *text3;
|
||||
static StThemeNode *text4;
|
||||
static StThemeNode *group3;
|
||||
static StThemeNode *cairo_texture;
|
||||
static gboolean fail;
|
||||
|
||||
static const char *test;
|
||||
|
||||
static void
|
||||
assert_font (StThemeNode *node,
|
||||
const char *node_description,
|
||||
const char *expected)
|
||||
{
|
||||
char *value = pango_font_description_to_string (st_theme_node_get_font (node));
|
||||
|
||||
if (strcmp (expected, value) != 0)
|
||||
{
|
||||
g_print ("%s: %s.font: expected: %s, got: %s\n",
|
||||
test, node_description, expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
static char *
|
||||
text_decoration_to_string (StTextDecoration decoration)
|
||||
{
|
||||
GString *result = g_string_new (NULL);
|
||||
|
||||
if (decoration & ST_TEXT_DECORATION_UNDERLINE)
|
||||
g_string_append(result, " underline");
|
||||
if (decoration & ST_TEXT_DECORATION_OVERLINE)
|
||||
g_string_append(result, " overline");
|
||||
if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
|
||||
g_string_append(result, " line_through");
|
||||
if (decoration & ST_TEXT_DECORATION_BLINK)
|
||||
g_string_append(result, " blink");
|
||||
|
||||
if (result->len > 0)
|
||||
g_string_erase (result, 0, 1);
|
||||
else
|
||||
g_string_append(result, "none");
|
||||
|
||||
return g_string_free (result, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_text_decoration (StThemeNode *node,
|
||||
const char *node_description,
|
||||
StTextDecoration expected)
|
||||
{
|
||||
StTextDecoration value = st_theme_node_get_text_decoration (node);
|
||||
if (expected != value)
|
||||
{
|
||||
char *es = text_decoration_to_string (expected);
|
||||
char *vs = text_decoration_to_string (value);
|
||||
|
||||
g_print ("%s: %s.text-decoration: expected: %s, got: %s\n",
|
||||
test, node_description, es, vs);
|
||||
fail = TRUE;
|
||||
|
||||
g_free (es);
|
||||
g_free (vs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
assert_foreground_color (StThemeNode *node,
|
||||
const char *node_description,
|
||||
guint32 expected)
|
||||
{
|
||||
ClutterColor color;
|
||||
st_theme_node_get_foreground_color (node, &color);
|
||||
guint32 value = clutter_color_to_pixel (&color);
|
||||
|
||||
if (expected != value)
|
||||
{
|
||||
g_print ("%s: %s.color: expected: #%08x, got: #%08x\n",
|
||||
test, node_description, expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
assert_background_color (StThemeNode *node,
|
||||
const char *node_description,
|
||||
guint32 expected)
|
||||
{
|
||||
ClutterColor color;
|
||||
st_theme_node_get_background_color (node, &color);
|
||||
guint32 value = clutter_color_to_pixel (&color);
|
||||
|
||||
if (expected != value)
|
||||
{
|
||||
g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n",
|
||||
test, node_description, expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
side_to_string (StSide side)
|
||||
{
|
||||
switch (side)
|
||||
{
|
||||
case ST_SIDE_TOP:
|
||||
return "top";
|
||||
case ST_SIDE_RIGHT:
|
||||
return "right";
|
||||
case ST_SIDE_BOTTOM:
|
||||
return "bottom";
|
||||
case ST_SIDE_LEFT:
|
||||
return "left";
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
static void
|
||||
assert_border_color (StThemeNode *node,
|
||||
const char *node_description,
|
||||
StSide side,
|
||||
guint32 expected)
|
||||
{
|
||||
ClutterColor color;
|
||||
st_theme_node_get_border_color (node, side, &color);
|
||||
guint32 value = clutter_color_to_pixel (&color);
|
||||
|
||||
if (expected != value)
|
||||
{
|
||||
g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n",
|
||||
test, node_description, side_to_string (side), expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
assert_background_image (StThemeNode *node,
|
||||
const char *node_description,
|
||||
const char *expected)
|
||||
{
|
||||
const char *value = st_theme_node_get_background_image (node);
|
||||
if (expected == NULL)
|
||||
expected = "(null)";
|
||||
if (value == NULL)
|
||||
value = "(null)";
|
||||
|
||||
if (strcmp (expected, value) != 0)
|
||||
{
|
||||
g_print ("%s: %s.background-image: expected: %s, got: %s\n",
|
||||
test, node_description, expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
#define LENGTH_EPSILON 0.001
|
||||
|
||||
static void
|
||||
assert_length (const char *node_description,
|
||||
const char *property_description,
|
||||
double expected,
|
||||
double value)
|
||||
{
|
||||
if (fabs (expected - value) > LENGTH_EPSILON)
|
||||
{
|
||||
g_print ("%s %s.%s: expected: %3f, got: %3f\n",
|
||||
test, node_description, property_description, expected, value);
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_defaults (void)
|
||||
{
|
||||
test = "defaults";
|
||||
/* font comes from context */
|
||||
assert_font (root, "stage", "sans-serif 12");
|
||||
/* black is the default foreground color */
|
||||
assert_foreground_color (root, "stage", 0x00000ff);
|
||||
}
|
||||
|
||||
static void
|
||||
test_lengths (void)
|
||||
{
|
||||
test = "lengths";
|
||||
/* 12pt == 16px at 96dpi */
|
||||
assert_length ("group1", "padding-top", 16.,
|
||||
st_theme_node_get_padding (group1, ST_SIDE_TOP));
|
||||
/* 12px == 12px */
|
||||
assert_length ("group1", "padding-right", 12.,
|
||||
st_theme_node_get_padding (group1, ST_SIDE_RIGHT));
|
||||
/* 2em == 32px (with a 12pt font) */
|
||||
assert_length ("group1", "padding-bottom", 32.,
|
||||
st_theme_node_get_padding (group1, ST_SIDE_BOTTOM));
|
||||
/* 1in == 72pt == 96px, at 96dpi */
|
||||
assert_length ("group1", "padding-left", 96.,
|
||||
st_theme_node_get_padding (group1, ST_SIDE_LEFT));
|
||||
}
|
||||
|
||||
static void
|
||||
test_classes (void)
|
||||
{
|
||||
test = "classes";
|
||||
/* .special-text class overrides size and style;
|
||||
* the ClutterTexture.special-text selector doesn't match */
|
||||
assert_font (text1, "text1", "sans-serif Italic 32px");
|
||||
}
|
||||
|
||||
static void
|
||||
test_type_inheritance (void)
|
||||
{
|
||||
test = "type_inheritance";
|
||||
/* From ClutterTexture element selector */
|
||||
assert_length ("cairoTexture", "padding-top", 10.,
|
||||
st_theme_node_get_padding (cairo_texture, ST_SIDE_TOP));
|
||||
/* From ClutterCairoTexture element selector */
|
||||
assert_length ("cairoTexture", "padding-right", 20.,
|
||||
st_theme_node_get_padding (cairo_texture, ST_SIDE_RIGHT));
|
||||
}
|
||||
|
||||
static void
|
||||
test_adjacent_selector (void)
|
||||
{
|
||||
test = "adjacent_selector";
|
||||
/* #group1 > #text1 matches text1 */
|
||||
assert_foreground_color (text1, "text1", 0x00ff00ff);
|
||||
/* stage > #text2 doesn't match text2 */
|
||||
assert_foreground_color (text2, "text2", 0x000000ff);
|
||||
}
|
||||
|
||||
static void
|
||||
test_padding (void)
|
||||
{
|
||||
test = "padding";
|
||||
/* Test that a 4-sided padding property assigns the right paddings to
|
||||
* all sides */
|
||||
assert_length ("group2", "padding-top", 1.,
|
||||
st_theme_node_get_padding (group2, ST_SIDE_TOP));
|
||||
assert_length ("group2", "padding-right", 2.,
|
||||
st_theme_node_get_padding (group2, ST_SIDE_RIGHT));
|
||||
assert_length ("group2", "padding-bottom", 3.,
|
||||
st_theme_node_get_padding (group2, ST_SIDE_BOTTOM));
|
||||
assert_length ("group2", "padding-left", 4.,
|
||||
st_theme_node_get_padding (group2, ST_SIDE_LEFT));
|
||||
}
|
||||
|
||||
static void
|
||||
test_border (void)
|
||||
{
|
||||
test = "border";
|
||||
|
||||
/* group2 is defined as having a thin black border along the top three
|
||||
* sides with rounded joins, then a square-joined green border at the
|
||||
* botttom
|
||||
*/
|
||||
|
||||
assert_length ("group2", "border-top-width", 2.,
|
||||
st_theme_node_get_border_width (group2, ST_SIDE_TOP));
|
||||
assert_length ("group2", "border-right-width", 2.,
|
||||
st_theme_node_get_border_width (group2, ST_SIDE_RIGHT));
|
||||
assert_length ("group2", "border-bottom-width", 5.,
|
||||
st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM));
|
||||
assert_length ("group2", "border-left-width", 2.,
|
||||
st_theme_node_get_border_width (group2, ST_SIDE_LEFT));
|
||||
|
||||
assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff);
|
||||
assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff);
|
||||
assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff);
|
||||
assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff);
|
||||
|
||||
assert_length ("group2", "border-radius-topleft", 10.,
|
||||
st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT));
|
||||
assert_length ("group2", "border-radius-topright", 10.,
|
||||
st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT));
|
||||
assert_length ("group2", "border-radius-bottomright", 0.,
|
||||
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT));
|
||||
assert_length ("group2", "border-radius-bottomleft", 0.,
|
||||
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT));
|
||||
}
|
||||
|
||||
static void
|
||||
test_background (void)
|
||||
{
|
||||
test = "background";
|
||||
/* group1 has a background: shortcut property setting color and image */
|
||||
assert_background_color (group1, "group1", 0xff0000ff);
|
||||
assert_background_image (group1, "group1", "st/some-background.png");
|
||||
/* text1 inherits the background image but not the color */
|
||||
assert_background_color (text1, "text1", 0x00000000);
|
||||
assert_background_image (text1, "text1", "st/some-background.png");
|
||||
/* text1 inherits inherits both, but then background: none overrides both */
|
||||
assert_background_color (text2, "text2", 0x00000000);
|
||||
assert_background_image (text2, "text2", NULL);
|
||||
/* background-image property */
|
||||
assert_background_image (group2, "group2", "st/other-background.png");
|
||||
}
|
||||
|
||||
static void
|
||||
test_font (void)
|
||||
{
|
||||
test = "font";
|
||||
/* font specified with font: */
|
||||
assert_font (group2, "group2", "serif Italic 12px");
|
||||
/* text3 inherits and overrides individually properties */
|
||||
assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px");
|
||||
}
|
||||
|
||||
static void
|
||||
test_pseudo_class (void)
|
||||
{
|
||||
test = "pseudo_class";
|
||||
/* text4 has :visited and :hover pseudo-classes, so should pick up both of these */
|
||||
assert_foreground_color (text4, "text4", 0x888888ff);
|
||||
assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE);
|
||||
/* :hover pseudo-class matches, but class doesn't match */
|
||||
assert_text_decoration (group3, "group3", 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_inline_style (void)
|
||||
{
|
||||
test = "inline_style";
|
||||
/* These properties come from the inline-style specified when creating the node */
|
||||
assert_foreground_color (text3, "text3", 0x00000ffff);
|
||||
assert_length ("text3", "padding-bottom", 12.,
|
||||
st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
StTheme *theme;
|
||||
StThemeContext *context;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
theme = st_theme_new ("st/test-theme.css",
|
||||
NULL, NULL);
|
||||
|
||||
context = st_theme_context_new ();
|
||||
st_theme_context_set_theme (context, theme);
|
||||
st_theme_context_set_resolution (context, 96.);
|
||||
st_theme_context_set_font (context,
|
||||
pango_font_description_from_string ("sans-serif 12"));
|
||||
|
||||
root = st_theme_context_get_root_node (context);
|
||||
group1 = st_theme_node_new (context, root, NULL,
|
||||
CLUTTER_TYPE_GROUP, "group1", NULL, NULL, NULL);
|
||||
text1 = st_theme_node_new (context, group1, NULL,
|
||||
CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
|
||||
text2 = st_theme_node_new (context, group1, NULL,
|
||||
CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
|
||||
group2 = st_theme_node_new (context, root, NULL,
|
||||
CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
|
||||
text3 = st_theme_node_new (context, group2, NULL,
|
||||
CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
|
||||
"color: #0000ff; padding-bottom: 12px;");
|
||||
text4 = st_theme_node_new (context, group2, NULL,
|
||||
CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
|
||||
group3 = st_theme_node_new (context, group2, NULL,
|
||||
CLUTTER_TYPE_GROUP, "group3", NULL, "hover", NULL);
|
||||
cairo_texture = st_theme_node_new (context, root, NULL,
|
||||
CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL);
|
||||
|
||||
test_defaults ();
|
||||
test_lengths ();
|
||||
test_classes ();
|
||||
test_type_inheritance ();
|
||||
test_adjacent_selector ();
|
||||
test_padding ();
|
||||
test_border ();
|
||||
test_background ();
|
||||
test_font ();
|
||||
test_pseudo_class ();
|
||||
test_inline_style ();
|
||||
|
||||
return fail ? 1 : 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user