animate iconify/reestore. yeah.
authorDana Jansens <danakj@orodu.net>
Sat, 5 May 2007 05:58:35 +0000 (05:58 +0000)
committerDana Jansens <danakj@orodu.net>
Sat, 5 May 2007 05:58:35 +0000 (05:58 +0000)
data/rc.xml.in
data/rc.xsd
openbox/client.c
openbox/client.h
openbox/config.c
openbox/config.h
openbox/event.c
openbox/frame.c
openbox/frame.h
openbox/prop.c
openbox/prop.h

index 69b2484..bfffdd7 100644 (file)
@@ -43,6 +43,7 @@
   -->
   <keepBorder>yes</keepBorder>
   <hideDisabled>no</hideDisabled>
+  <animateIconify>yes</animateIconify>
   <font place="ActiveWindow">
     <name>sans</name>
     <size>7</size>
index 4da392a..01fd525 100644 (file)
         <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">
index 3ebcc69..658b05d 100644 (file)
@@ -970,6 +970,7 @@ static void client_get_all(ObClient *self)
     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)
@@ -2022,6 +2023,22 @@ void client_update_user_time(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;
@@ -2707,9 +2724,23 @@ static void client_iconify_recursive(ObClient *self,
 
     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
index 1273a68..afdeac5 100644 (file)
@@ -287,6 +287,9 @@ struct _ObClient
     /*! The number of icons in icons */
     guint nicons;
 
+    /* Where the window should iconify to/from */
+    Rect icon_geometry;
+
     guint32 user_time;
 };
 
@@ -587,6 +590,8 @@ void client_update_strut(ObClient *self);
 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).
index 4e3f6f9..367fb2f 100644 (file)
@@ -42,6 +42,8 @@ gboolean config_theme_hidedisabled;
 
 gchar   *config_title_layout;
 
+gboolean config_animate_iconify;
+
 RrFont *config_font_activewindow;
 RrFont *config_font_inactivewindow;
 RrFont *config_font_menuitem;
@@ -454,6 +456,8 @@ static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
         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) {
@@ -805,6 +809,7 @@ void config_startup(ObParseInst *i)
 
     config_theme = NULL;
 
+    config_animate_iconify = TRUE;
     config_title_layout = g_strdup("NLIMC");
     config_theme_keepborder = TRUE;
     config_theme_hidedisabled = FALSE;
index e6a441b..f85f4f1 100644 (file)
@@ -88,6 +88,8 @@ extern gboolean config_theme_keepborder;
 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;
index badef3d..a0202bf 100644 (file)
@@ -1125,6 +1125,9 @@ static void event_handle_client(ObClient *client, XEvent *e)
         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);
         }
index 8feef93..8092e6c 100644 (file)
@@ -41,6 +41,9 @@
    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)
 
@@ -50,6 +53,7 @@ static gboolean flash_timeout(gpointer data);
 
 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)
@@ -476,9 +480,13 @@ void frame_adjust_area(ObFrame *self, gboolean moved,
                              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,
@@ -592,6 +600,9 @@ void frame_release_client(ObFrame *self, ObClient *client)
 
     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))
@@ -1019,3 +1030,103 @@ void frame_flash_stop(ObFrame *self)
 {
     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);
+    }
+}
index 19bade7..8f210b0 100644 (file)
@@ -27,6 +27,8 @@ typedef struct _ObFrame ObFrame;
 
 struct _ObClient;
 
+typedef void (*ObFrameIconifyAnimateFunc)(gpointer data);
+
 typedef enum {
     OB_FRAME_CONTEXT_NONE,
     OB_FRAME_CONTEXT_DESKTOP,
@@ -142,6 +144,17 @@ struct _ObFrame
     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);
@@ -178,4 +191,11 @@ void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h);
 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
index 2251987..e622c7d 100644 (file)
@@ -85,6 +85,7 @@ void prop_startup()
     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");
index f4d7f8b..49c342f 100644 (file)
@@ -93,6 +93,7 @@ typedef struct Atoms {
     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;