-->
<keepBorder>yes</keepBorder>
<hideDisabled>no</hideDisabled>
+ <animateIconify>yes</animateIconify>
<font place="ActiveWindow">
<name>sans</name>
<size>7</size>
<xsd:element minOccurs="0" name="titleNumber" type="ob:bool"/>
<xsd:element minOccurs="0" name="keepBorder" type="ob:bool"/>
<xsd:element minOccurs="0" name="hideDisabled" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="animateIconify" type="ob:bool"/>
<xsd:element minOccurs="0" name="font" type="ob:font"/>
</xsd:complexType>
<xsd:complexType name="font">
client_update_strut(self);
client_update_icons(self);
client_update_user_time(self);
+ client_update_icon_geometry(self);
}
static void client_get_startup_id(ObClient *self)
}
}
+void client_update_icon_geometry(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+
+ RECT_SET(self->icon_geometry, 0, 0, 0, 0);
+
+ if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
+ && num == 4)
+ {
+ /* don't let them set it with an area < 0 */
+ RECT_SET(self->icon_geometry, data[0], data[1],
+ MAX(data[2],0), MAX(data[3],0));
+ }
+}
+
static void client_get_client_machine(ObClient *self)
{
gchar *data = NULL;
if (changed) {
client_change_state(self);
- client_showhide(self);
- if (STRUT_EXISTS(self->strut))
- screen_update_areas();
+ if (iconic) {
+ if (ob_state() != OB_STATE_STARTING && config_animate_iconify) {
+ /* delay the showhide until the window is done the animation */
+ frame_begin_iconify_animation
+ (self->frame, iconic,
+ (ObFrameIconifyAnimateFunc)client_showhide, self);
+ /* but focus a new window now please */
+ focus_fallback(TRUE);
+ } else
+ client_showhide(self);
+ } else {
+ if (config_animate_iconify)
+ /* start the animation then show it, this way the whole window
+ doesnt get shown, just the first step of the animation */
+ frame_begin_iconify_animation(self->frame, iconic, NULL, NULL);
+ client_showhide(self);
+ }
}
/* iconify all direct transients, and deiconify all transients
/*! The number of icons in icons */
guint nicons;
+ /* Where the window should iconify to/from */
+ Rect icon_geometry;
+
guint32 user_time;
};
void client_update_icons(ObClient *self);
/*! Updates the window's user time */
void client_update_user_time(ObClient *self);
+/*! Updates the window's icon geometry (where to iconify to/from) */
+void client_update_icon_geometry(ObClient *self);
/*! Set up what decor should be shown on the window and what functions should
be allowed (ObClient::decorations and ObClient::functions).
gchar *config_title_layout;
+gboolean config_animate_iconify;
+
RrFont *config_font_activewindow;
RrFont *config_font_inactivewindow;
RrFont *config_font_menuitem;
config_theme_keepborder = parse_bool(doc, n);
if ((n = parse_find_node("hideDisabled", node)))
config_theme_hidedisabled = parse_bool(doc, n);
+ if ((n = parse_find_node("animateIconify", node)))
+ config_animate_iconify = parse_bool(doc, n);
n = parse_find_node("font", node);
while (n) {
config_theme = NULL;
+ config_animate_iconify = TRUE;
config_title_layout = g_strdup("NLIMC");
config_theme_keepborder = TRUE;
config_theme_hidedisabled = FALSE;
extern gboolean config_theme_hidedisabled;
/*! Titlebar button layout */
extern gchar *config_title_layout;
+/*! Animate windows iconifying and restoring */
+extern gboolean config_animate_iconify;
/*! The font for the active window's title */
extern RrFont *config_font_activewindow;
else if (msgtype == prop_atoms.net_wm_icon) {
client_update_icons(client);
}
+ else if (msgtype == prop_atoms.net_wm_icon_geometry) {
+ client_update_icon_geometry(client);
+ }
else if (msgtype == prop_atoms.net_wm_user_time) {
client_update_user_time(client);
}
from the frame. */
#define INNER_EVENTMASK (ButtonPressMask)
+#define FRAME_ANIMATE_ICONIFY_STEPS 20
+#define FRAME_ANIMATE_ICONIFY_TIME (G_USEC_PER_SEC/10)
+
#define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
f->cbwidth_y)
static void set_theme_statics(ObFrame *self);
static void free_theme_statics(ObFrame *self);
+static gboolean frame_animate_iconify(gpointer self);
static Window createWindow(Window parent, Visual *visual,
gulong mask, XSetWindowAttributes *attrib)
self->client->area.height);
}
- if (!fake) {
+ if (!fake && !self->iconify_animation_step) {
/* move and resize the top level frame.
- shading can change without being moved or resized */
+ shading can change without being moved or resized.
+
+ but don't do this during an iconify animation. it will be
+ reflected afterwards.
+ */
XMoveResizeWindow(ob_display, self->window,
self->area.x, self->area.y,
self->area.width - self->bwidth * 2,
g_assert(self->client == client);
+ /* if there was any animation going on, kill it */
+ ob_main_loop_timeout_remove(ob_main_loop, frame_animate_iconify);
+
/* check if the app has already reparented its window away */
while (XCheckTypedWindowEvent(ob_display, client->window,
ReparentNotify, &ev))
{
self->flashing = FALSE;
}
+
+static gboolean frame_animate_iconify(gpointer p)
+{
+ ObFrame *self = p;
+ gint step = self->iconify_animation_step;
+ gint absstep, nextstep;
+ gint x, y, w, h;
+ gint iconx, icony, iconw;
+
+ if (self->client->icon_geometry.width == 0) {
+ /* there is no icon geometry set so just go straight down */
+ Rect *a = screen_physical_area();
+ iconx = self->area.x + self->area.width / 2 + 32;
+ icony = a->y + a->width;
+ iconw = 64;
+ } else {
+ iconx = self->client->icon_geometry.x;
+ icony = self->client->icon_geometry.y;
+ iconw = self->client->icon_geometry.width;
+ }
+
+ if (step >= 0)
+ absstep = FRAME_ANIMATE_ICONIFY_STEPS - step + 1;
+ else
+ absstep = FRAME_ANIMATE_ICONIFY_STEPS + step + 1;
+
+ if (step >= 0) {
+ /* start where the frame is supposed to be */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width - self->bwidth * 2;
+ h = self->area.height - self->bwidth * 2;
+ } else {
+ /* start at the icon */
+ x = iconx;
+ y = icony;
+ w = iconw;
+ h = self->innersize.top; /* just the titlebar */
+ }
+
+ if (step != 0) {
+ gint dx, dy, dw;
+ dx = self->area.x - iconx;
+ dy = self->area.y - icony;
+ dw = self->area.width - self->bwidth * 2 - iconw;
+ /* if restoring, we move in the opposite direction */
+ if (step < 0) { dx = -dx; dy = -dy; dw = -dw; }
+ x = x - dx / FRAME_ANIMATE_ICONIFY_STEPS * absstep;
+ y = y - dy / FRAME_ANIMATE_ICONIFY_STEPS * absstep;
+ w = w - dw / FRAME_ANIMATE_ICONIFY_STEPS * absstep;
+ h = self->innersize.top; /* just the titlebar */
+ }
+
+ /* move one step forward */
+ self->iconify_animation_step = step + (step < 0 ? 1 : (step > 0 ? -1 : 0));
+
+ /* call the callback when it's done */
+ if (step == 0 && self->iconify_animation_cb)
+ self->iconify_animation_cb(self->iconify_animation_data);
+
+ /* move to the next spot (after the callback for the animation ending) */
+ XMoveResizeWindow(ob_display, self->window, x, y, w, h);
+ XFlush(ob_display);
+
+ return step != 0; /* repeat until step is 0 */
+}
+
+void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying,
+ ObFrameIconifyAnimateFunc callback,
+ gpointer data)
+{
+ if (iconifying) {
+ if (self->iconify_animation_step == 0) /* wasnt doing anything */
+ self->iconify_animation_step = FRAME_ANIMATE_ICONIFY_STEPS;
+ else if (self->iconify_animation_step < 0) /* was deiconifying */
+ self->iconify_animation_step =
+ FRAME_ANIMATE_ICONIFY_STEPS + self->iconify_animation_step;
+ } else {
+ if (self->iconify_animation_step == 0) /* wasnt doing anything */
+ self->iconify_animation_step = -FRAME_ANIMATE_ICONIFY_STEPS;
+ else if (self->iconify_animation_step > 0) /* was iconifying */
+ self->iconify_animation_step =
+ -FRAME_ANIMATE_ICONIFY_STEPS + self->iconify_animation_step;
+ }
+ self->iconify_animation_cb = callback;
+ self->iconify_animation_data = data;
+
+ if (self->iconify_animation_step == FRAME_ANIMATE_ICONIFY_STEPS ||
+ self->iconify_animation_step == -FRAME_ANIMATE_ICONIFY_STEPS)
+ {
+ ob_main_loop_timeout_remove(ob_main_loop, frame_animate_iconify);
+ ob_main_loop_timeout_add(ob_main_loop,
+ FRAME_ANIMATE_ICONIFY_TIME /
+ FRAME_ANIMATE_ICONIFY_STEPS,
+ frame_animate_iconify, self,
+ g_direct_equal, NULL);
+ /* do the first step */
+ frame_animate_iconify(self);
+ }
+}
struct _ObClient;
+typedef void (*ObFrameIconifyAnimateFunc)(gpointer data);
+
typedef enum {
OB_FRAME_CONTEXT_NONE,
OB_FRAME_CONTEXT_DESKTOP,
gboolean flashing;
gboolean flash_on;
GTimeVal flash_end;
+
+ /*! The step which the client is currently in for animating iconify and
+ restore.
+ 0 means that it is not animating. FRAME_ANIMATE_ICONIFY_STEPS is the
+ first step for iconifying, and -FRAME_ANIMATE_ICONIFY_STEPS is the
+ forst step for restoring. It counts towards 0 either way. Visually,
+ +x == -(FRAME_ANIMATE_ICONIFY_STEPS-x+1)
+ */
+ gint iconify_animation_step;
+ ObFrameIconifyAnimateFunc iconify_animation_cb;
+ gpointer iconify_animation_data;
};
ObFrame *frame_new(struct _ObClient *c);
void frame_flash_start(ObFrame *self);
void frame_flash_stop(ObFrame *self);
+/*! Start an animation for iconifying or restoring a frame. The callback
+ will be called when the animation finishes. But if another animation is
+ started in the meantime, the callback will never get called. */
+void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying,
+ ObFrameIconifyAnimateFunc callback,
+ gpointer data);
+
#endif
CREATE(net_wm_strut, "_NET_WM_STRUT");
CREATE(net_wm_strut_partial, "_NET_WM_STRUT_PARTIAL");
CREATE(net_wm_icon, "_NET_WM_ICON");
+ CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY");
/* CREATE(net_wm_pid, "_NET_WM_PID"); */
CREATE(net_wm_allowed_actions, "_NET_WM_ALLOWED_ACTIONS");
CREATE(net_wm_user_time, "_NET_WM_USER_TIME");
Atom net_wm_strut;
Atom net_wm_strut_partial;
Atom net_wm_icon;
+ Atom net_wm_icon_geometry;
/* Atom net_wm_pid; */
Atom net_wm_allowed_actions;
Atom net_wm_user_time;