diff options
| author | Stefano Karapetsas <[email protected]> | 2013-05-31 16:22:39 +0200 | 
|---|---|---|
| committer | Stefano Karapetsas <[email protected]> | 2013-05-31 16:22:39 +0200 | 
| commit | a87157176ca6e01c8c4047999ee584f00b63c11e (patch) | |
| tree | 80e928a800b4b54f6831a6b38a952014c7f5ae5c | |
| parent | cc760e2586cd0f98f8c60382ab8ff1f7373d9c1d (diff) | |
| download | marco-a87157176ca6e01c8c4047999ee584f00b63c11e.tar.bz2 marco-a87157176ca6e01c8c4047999ee584f00b63c11e.tar.xz | |
Implement side-by-side tiling
Patch by Florian Müllner for Metacity
https://bugzilla.gnome.org/show_bug.cgi?id=607694
When dragging a window over a screen edge and dropping it there,
maximize it vertically and scale it horizontally to cover the
corresponding half of the current monitor.
Whenever a "hot area" which triggers this behavior is entered, an
indication of window's target size is displayed after a short delay
to avoid distraction when moving a window between monitors.
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/core/constraints.c | 65 | ||||
| -rw-r--r-- | src/core/core.c | 30 | ||||
| -rw-r--r-- | src/core/prefs.c | 16 | ||||
| -rw-r--r-- | src/core/screen-private.h | 5 | ||||
| -rw-r--r-- | src/core/screen.c | 66 | ||||
| -rw-r--r-- | src/core/window-private.h | 18 | ||||
| -rw-r--r-- | src/core/window.c | 175 | ||||
| -rw-r--r-- | src/include/core.h | 4 | ||||
| -rw-r--r-- | src/include/prefs.h | 2 | ||||
| -rw-r--r-- | src/include/tile-preview.h | 37 | ||||
| -rw-r--r-- | src/include/ui.h | 1 | ||||
| -rw-r--r-- | src/org.mate.marco.gschema.xml | 5 | ||||
| -rw-r--r-- | src/ui/tile-preview.c | 251 | 
14 files changed, 650 insertions, 27 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index a7b01237..93263611 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,6 +98,8 @@ marco_SOURCES = \  	include/resizepopup.h \  	ui/tabpopup.c \  	include/tabpopup.h \ +	ui/tile-preview.c \ +	include/tile-preview.h \  	ui/theme-parser.c \  	ui/theme-parser.h \  	ui/theme.c \ diff --git a/src/core/constraints.c b/src/core/constraints.c index 16d9b107..a79f858a 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -98,6 +98,7 @@ typedef enum    PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,    PRIORITY_SIZE_HINTS_INCREMENTS = 1,    PRIORITY_MAXIMIZATION = 2, +  PRIORITY_TILING = 2,    PRIORITY_FULLSCREEN = 2,    PRIORITY_SIZE_HINTS_LIMITS = 3,    PRIORITY_TITLEBAR_VISIBLE = 4, @@ -145,6 +146,10 @@ static gboolean constrain_maximization       (MetaWindow         *window,                                                ConstraintInfo     *info,                                                ConstraintPriority  priority,                                                gboolean            check_only); +static gboolean constrain_tiling             (MetaWindow         *window, +                                              ConstraintInfo     *info, +                                              ConstraintPriority  priority, +                                              gboolean            check_only);  static gboolean constrain_fullscreen         (MetaWindow         *window,                                                ConstraintInfo     *info,                                                ConstraintPriority  priority, @@ -211,6 +216,7 @@ typedef struct {  static const Constraint all_constraints[] = {    {constrain_maximization,       "constrain_maximization"}, +  {constrain_tiling,             "constrain_tiling"},    {constrain_fullscreen,         "constrain_fullscreen"},    {constrain_size_increments,    "constrain_size_increments"},    {constrain_size_limits,        "constrain_size_limits"}, @@ -731,7 +737,8 @@ constrain_maximization (MetaWindow         *window,      return TRUE;    /* Determine whether constraint applies; exit if it doesn't */ -  if (!window->maximized_horizontally && !window->maximized_vertically) +  if ((!window->maximized_horizontally && !window->maximized_vertically) || +      META_WINDOW_TILED (window))      return TRUE;    /* Calculate target_size = maximized size of (window + frame) */ @@ -800,6 +807,58 @@ constrain_maximization (MetaWindow         *window,  }  static gboolean +constrain_tiling (MetaWindow         *window, +                  ConstraintInfo     *info, +                  ConstraintPriority  priority, +                  gboolean            check_only) +{ +  MetaRectangle target_size; +  MetaRectangle min_size, max_size; +  gboolean hminbad, vminbad; +  gboolean horiz_equal, vert_equal; +  gboolean constraint_already_satisfied; + +  if (priority > PRIORITY_TILING) +    return TRUE; + +  /* Determine whether constraint applies; exit if it doesn't */ +  if (!META_WINDOW_TILED (window)) +    return TRUE; + +  /* Calculate target_size - as the tile previews need this as well, we +   * use an external function for the actual calculation +   */ +  meta_window_get_current_tile_area (window, &target_size); +  unextend_by_frame (&target_size, info->fgeom); + +  /* Check min size constraints; max size constraints are ignored as for +   * maximized windows. +   */ +  get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size); +  hminbad = target_size.width < min_size.width; +  vminbad = target_size.height < min_size.height; +  if (hminbad || vminbad) +    return TRUE; + +  /* Determine whether constraint is already satisfied; exit if it is */ +  horiz_equal = target_size.x      == info->current.x && +                target_size.width  == info->current.width; +  vert_equal  = target_size.y      == info->current.y && +                target_size.height == info->current.height; +  constraint_already_satisfied = horiz_equal && vert_equal; +  if (check_only || constraint_already_satisfied) +    return constraint_already_satisfied; + +  /*** Enforce constraint ***/ +  info->current.x      = target_size.x; +  info->current.width  = target_size.width; +  info->current.y      = target_size.y; +  info->current.height = target_size.height; + +  return TRUE; +} + +static gboolean  constrain_fullscreen (MetaWindow         *window,                        ConstraintInfo     *info,                        ConstraintPriority  priority, @@ -850,7 +909,7 @@ constrain_size_increments (MetaWindow         *window,    /* Determine whether constraint applies; exit if it doesn't */    if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||  -      info->action_type == ACTION_MOVE) +      META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)      return TRUE;    /* Determine whether constraint is already satisfied; exit if it is */ @@ -981,7 +1040,7 @@ constrain_aspect_ratio (MetaWindow         *window,    constraints_are_inconsistent = minr > maxr;    if (constraints_are_inconsistent ||        META_WINDOW_MAXIMIZED (window) || window->fullscreen ||  -      info->action_type == ACTION_MOVE) +      META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)      return TRUE;    /* Determine whether constraint is already satisfied; exit if it is.  We diff --git a/src/core/core.c b/src/core/core.c index 76e5548b..c8fa02b7 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -28,6 +28,7 @@  #include "frame-private.h"  #include "workspace.h"  #include "prefs.h" +#include "errors.h"  /* Looks up the MetaWindow representing the frame of the given X window.   * Used as a helper function by a bunch of the functions below. @@ -297,6 +298,35 @@ meta_core_user_lower_and_unfocus (Display *xdisplay,  }  void +meta_core_lower_beneath_focus_window (Display *xdisplay, +                                      Window   xwindow, +                                      guint32  timestamp) +{ +  XWindowChanges changes; +  MetaDisplay *display; +  MetaScreen *screen; +  MetaWindow *focus_window; + +  display = meta_display_for_x_display (xdisplay); +  screen = meta_display_screen_for_xwindow (display, xwindow); +  focus_window = meta_stack_get_top (screen->stack); + +  if (focus_window == NULL) +    return; + +  changes.stack_mode = Below; +  changes.sibling = focus_window->frame ? focus_window->frame->xwindow +                                        : focus_window->xwindow; + +  meta_error_trap_push (display); +  XConfigureWindow (xdisplay, +                    xwindow, +                    CWSibling | CWStackMode, +                    &changes); +  meta_error_trap_pop (display, FALSE); +} + +void  meta_core_user_focus (Display *xdisplay,                        Window   frame_xwindow,                        guint32  timestamp) diff --git a/src/core/prefs.c b/src/core/prefs.c index 116a9bb8..5f46b554 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -117,6 +117,7 @@ static gboolean compositing_fast_alt_tab = FALSE;  static gboolean resize_with_right_button = FALSE;  static gboolean center_new_windows = FALSE;  static gboolean force_fullscreen = TRUE; +static gboolean side_by_side_tiling = FALSE;  static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;  static MetaButtonLayout button_layout; @@ -401,6 +402,12 @@ static MetaBoolPreference preferences_bool[] =        ¢er_new_windows,        FALSE,      }, +    { "side-by-side-tiling", +      KEY_GENERAL_SCHEMA, +      META_PREF_SIDE_BY_SIDE_TILING, +      &side_by_side_tiling, +      FALSE, +    },      { NULL, NULL, 0, NULL, FALSE },    }; @@ -1545,6 +1552,9 @@ meta_preference_to_string (MetaPreference pref)      case META_PREF_FORCE_FULLSCREEN:        return "FORCE_FULLSCREEN"; + +    case META_PREF_SIDE_BY_SIDE_TILING: +      return "SIDE_BY_SIDE_TILING";      }    return "(unknown)"; @@ -2202,6 +2212,12 @@ meta_prefs_get_center_new_windows (void)      return center_new_windows;  } +gboolean +meta_prefs_get_side_by_side_tiling () +{ +  return side_by_side_tiling; +} +  guint  meta_prefs_get_mouse_button_resize (void)  { diff --git a/src/core/screen-private.h b/src/core/screen-private.h index 77ea457f..8eb02d00 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -79,6 +79,9 @@ struct _MetaScreen    MetaRectangle rect;  /* Size of screen; rect.x & rect.y are always 0 */    MetaUI *ui;    MetaTabPopup *tab_popup; +  MetaTilePreview *tile_preview; +   +  guint tile_preview_timeout_id;    MetaWorkspace *active_workspace; @@ -160,6 +163,8 @@ void          meta_screen_ensure_tab_popup    (MetaScreen                 *scree                                                 MetaTabList                 list_type,                                                 MetaTabShowType             show_type);  void          meta_screen_ensure_workspace_popup (MetaScreen *screen); +void          meta_screen_tile_preview_update          (MetaScreen    *screen, +                                                        gboolean       delay);  MetaWindow*   meta_screen_get_mouse_window     (MetaScreen                 *screen,                                                  MetaWindow                 *not_this_one); diff --git a/src/core/screen.c b/src/core/screen.c index e8fce40a..eefe58f2 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -582,7 +582,10 @@ meta_screen_new (MetaDisplay *display,                              screen->xscreen);    screen->tab_popup = NULL; -   +  screen->tile_preview = NULL; + +  screen->tile_preview_timeout_id = 0; +    screen->stack = meta_stack_new (screen);    meta_prefs_add_listener (prefs_changed_callback, screen); @@ -691,7 +694,13 @@ meta_screen_free (MetaScreen *screen,    if (screen->xinerama_infos)      g_free (screen->xinerama_infos); -   + +  if (screen->tile_preview_timeout_id) +    g_source_remove (screen->tile_preview_timeout_id); + +  if (screen->tile_preview) +    meta_tile_preview_free (screen->tile_preview); +    g_free (screen->screen_name);    g_free (screen); @@ -1451,6 +1460,59 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen)    /* don't show tab popup, since proper space isn't selected yet */  } +static gboolean +meta_screen_tile_preview_update_timeout (gpointer data) +{ +  MetaScreen *screen = data; +  MetaWindow *window = screen->display->grab_window; +  gboolean composited = screen->display->compositor != NULL; + +  screen->tile_preview_timeout_id = 0; + +  if (!screen->tile_preview) +    screen->tile_preview = meta_tile_preview_new (screen->number, +                                                  composited); + +  if (window +      && !META_WINDOW_TILED (window) +      && window->tile_mode != META_TILE_NONE) +    { +      MetaRectangle tile_rect; + +      meta_window_get_current_tile_area (window, &tile_rect); +      meta_tile_preview_show (screen->tile_preview, &tile_rect); +    } +  else +    meta_tile_preview_hide (screen->tile_preview); + +  return FALSE; +} + +#define TILE_PREVIEW_TIMEOUT_MS 200 + +void +meta_screen_tile_preview_update (MetaScreen *screen, +                                 gboolean    delay) +{ +  if (delay) +    { +      if (screen->tile_preview_timeout_id > 0) +        return; + +      screen->tile_preview_timeout_id = +        g_timeout_add (TILE_PREVIEW_TIMEOUT_MS, +                       meta_screen_tile_preview_update_timeout, +                       screen); +    } +  else +    { +      if (screen->tile_preview_timeout_id > 0) +        g_source_remove (screen->tile_preview_timeout_id); + +      meta_screen_tile_preview_update_timeout ((gpointer)screen); +    } +} +  MetaWindow*  meta_screen_get_mouse_window (MetaScreen  *screen,                                MetaWindow  *not_this_one) diff --git a/src/core/window-private.h b/src/core/window-private.h index 447a2c4b..02c518e0 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -83,6 +83,12 @@ typedef enum {  #define NUMBER_OF_QUEUES 3 +typedef enum { +  META_TILE_NONE, +  META_TILE_LEFT, +  META_TILE_RIGHT +} MetaTileMode; +  struct _MetaWindow  {    MetaDisplay *display; @@ -138,6 +144,11 @@ struct _MetaWindow    guint maximize_vertically_after_placement : 1;    guint minimize_after_placement : 1; +  /* The current or requested tile mode. If maximized_vertically is true, +   * this is the current mode. If not, it is the mode which will be +   * requested after the window grab is released */ +  guint tile_mode : 2; +    /* Whether we're shaded */    guint shaded : 1; @@ -383,8 +394,11 @@ struct _MetaWindow                                          (w)->maximized_vertically)  #define META_WINDOW_MAXIMIZED_VERTICALLY(w)    ((w)->maximized_vertically)  #define META_WINDOW_MAXIMIZED_HORIZONTALLY(w)  ((w)->maximized_horizontally) +#define META_WINDOW_TILED(w)           ((w)->maximized_vertically && \ +                                        !(w)->maximized_horizontally && \ +                                        (w)->tile_mode != META_TILE_NONE)  #define META_WINDOW_ALLOWS_MOVE(w)     ((w)->has_move_func && !(w)->fullscreen) -#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !(w)->fullscreen && !(w)->shaded) +#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !META_WINDOW_TILED(w) && !(w)->fullscreen && !(w)->shaded)  #define META_WINDOW_ALLOWS_RESIZE(w)   (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) &&                \                                          (((w)->size_hints.min_width < (w)->size_hints.max_width) ||  \                                           ((w)->size_hints.min_height < (w)->size_hints.max_height))) @@ -575,6 +589,8 @@ void meta_window_get_work_area_for_xinerama     (MetaWindow    *window,  void meta_window_get_work_area_all_xineramas    (MetaWindow    *window,                                                   MetaRectangle *area); +void meta_window_get_current_tile_area         (MetaWindow    *window, +                                                MetaRectangle *tile_area);  gboolean meta_window_same_application (MetaWindow *window,                                         MetaWindow *other_window); diff --git a/src/core/window.c b/src/core/window.c index d997bae7..7a7f6bec 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -470,6 +470,7 @@ meta_window_new_with_attrs (MetaDisplay       *display,    window->require_on_single_xinerama = TRUE;    window->require_titlebar_visible = TRUE;    window->on_all_workspaces = FALSE; +  window->tile_mode = META_TILE_NONE;    window->shaded = FALSE;    window->initially_iconic = FALSE;    window->minimized = FALSE; @@ -2489,7 +2490,7 @@ ensure_size_hints_satisfied (MetaRectangle    *rect,  static void  meta_window_save_rect (MetaWindow *window)  { -  if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen)) +  if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))      {        /* save size/pos as appropriate args for move_resize */        if (!window->maximized_horizontally) @@ -2531,7 +2532,7 @@ force_save_user_window_placement (MetaWindow *window)  static void  save_user_window_placement (MetaWindow *window)  { -  if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen)) +  if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))      {        MetaRectangle user_rect; @@ -2596,6 +2597,7 @@ void  meta_window_maximize (MetaWindow        *window,                        MetaMaximizeFlags  directions)  { +  MetaRectangle *saved_rect = NULL;    /* At least one of the two directions ought to be set */    gboolean maximize_horizontally, maximize_vertically;    maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; @@ -2631,9 +2633,16 @@ meta_window_maximize (MetaWindow        *window,  	  return;  	} +      if (window->tile_mode != META_TILE_NONE) +        { +          saved_rect = &window->saved_rect; + +          window->maximized_vertically = FALSE; +        } +        meta_window_maximize_internal (window,                                       directions, -                                     NULL); +                                     saved_rect);        /* move_resize with new maximization constraints         */ @@ -2673,12 +2682,64 @@ unmaximize_window_before_freeing (MetaWindow        *window)      }  } +static void +meta_window_tile (MetaWindow *window) +{ +  /* Don't do anything if no tiling is requested */ +  if (window->tile_mode == META_TILE_NONE) +    return; + +  meta_window_maximize_internal (window, META_MAXIMIZE_VERTICAL, NULL); +  meta_screen_tile_preview_update (window->screen, FALSE); + +  /* move_resize with new tiling constraints +   */ +  meta_window_queue (window, META_QUEUE_MOVE_RESIZE); +} + +static gboolean +meta_window_can_tile (MetaWindow *window) +{ +  const MetaXineramaScreenInfo *monitor; +  MetaRectangle tile_area; + +  if (!META_WINDOW_ALLOWS_RESIZE (window)) +    return FALSE; + +  monitor = meta_screen_get_current_xinerama (window->screen); +  meta_window_get_work_area_for_xinerama (window, monitor->number, &tile_area); + +  tile_area.width /= 2; + +  if (window->frame) +    { +      MetaFrameGeometry fgeom; + +      meta_frame_calc_geometry (window->frame, &fgeom); + +      tile_area.width  -= (fgeom.left_width + fgeom.right_width); +      tile_area.height -= (fgeom.top_height + fgeom.bottom_height); +    } + +  return tile_area.width >= window->size_hints.min_width && +         tile_area.height >= window->size_hints.min_height; +} +  void  meta_window_unmaximize (MetaWindow        *window,                          MetaMaximizeFlags  directions)  {    /* At least one of the two directions ought to be set */    gboolean unmaximize_horizontally, unmaximize_vertically; + +  /* Restore tiling if necessary */ +  if (window->tile_mode != META_TILE_NONE) +    { +      window->maximized_horizontally = FALSE; +      meta_window_tile (window); +      return; +    } +    unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;    unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;    g_assert (unmaximize_horizontally || unmaximize_vertically); @@ -2723,17 +2784,6 @@ meta_window_unmaximize (MetaWindow        *window,         */        ensure_size_hints_satisfied (&target_rect, &window->size_hints); -      /* When we unmaximize, if we're doing a mouse move also we could -       * get the window suddenly jumping to the upper left corner of -       * the workspace, since that's where it was when the grab op -       * started.  So we need to update the grab state. -       */ -      if (meta_grab_op_is_moving (window->display->grab_op) && -          window->display->grab_window == window) -        { -          window->display->grab_anchor_window_pos = target_rect; -        } -        meta_window_move_resize (window,                                 FALSE,                                 target_rect.x, @@ -2745,6 +2795,19 @@ meta_window_unmaximize (MetaWindow        *window,         */        force_save_user_window_placement (window); +      /* When we unmaximize, if we're doing a mouse move also we could +       * get the window suddenly jumping to the upper left corner of +       * the workspace, since that's where it was when the grab op +       * started.  So we need to update the grab state. We have to do +       * it after the actual operation, as the window may have been moved +       * by constraints. +       */ +      if (meta_grab_op_is_moving (window->display->grab_op) && +          window->display->grab_window == window) +        { +          window->display->grab_anchor_window_pos = window->user_rect; +        } +        if (window->display->grab_wireframe_active)          {            window->display->grab_wireframe_rect = target_rect; @@ -6898,20 +6961,58 @@ update_move (MetaWindow  *window,    if (dx == 0 && dy == 0)      return; -  /* shake loose (unmaximize) maximized window if dragged beyond the threshold -   * in the Y direction. You can't pull a window loose via X motion. +  /* Originally for detaching maximized windows, but we use this +   * for the zones at the sides of the monitor where trigger tiling +   * because it's about the right size     */  #define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6    shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *      DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR; -  if (META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) + +  if (meta_prefs_get_side_by_side_tiling () && +      meta_window_can_tile (window)) +    { +      const MetaXineramaScreenInfo *monitor; +      MetaRectangle work_area; + +      /* For tiling we are interested in the work area of the monitor where +       * the pointer is located. +       * Also see comment in meta_window_get_current_tile_area() +       */ +      monitor = meta_screen_get_current_xinerama (window->screen); +      meta_window_get_work_area_for_xinerama (window, +                                              monitor->number, +                                              &work_area); + +      if (y >= monitor->rect.y && +          y < (monitor->rect.y + monitor->rect.height)) +        { +          /* check if cursor is near an edge of the work area */ +          if (x >= monitor->rect.x && x < (work_area.x + shake_threshold)) +            window->tile_mode = META_TILE_LEFT; +          else if (x >= work_area.x + work_area.width - shake_threshold && +                   x < (monitor->rect.x + monitor->rect.width)) +            window->tile_mode = META_TILE_RIGHT; +          else +            window->tile_mode = META_TILE_NONE; +        } +    } + +  /* shake loose (unmaximize) maximized or tiled window if dragged beyond +   * the threshold in the Y direction. Tiled windows can also be pulled +   * loose via X motion. +   */ + +  if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) || +      (META_WINDOW_TILED (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))      {        double prop;        /* Shake loose */ -      window->shaken_loose = TRUE; +      window->shaken_loose = META_WINDOW_MAXIMIZED (window); +      window->tile_mode = META_TILE_NONE;        /* move the unmaximized window to the cursor */        prop = @@ -6995,13 +7096,20 @@ update_move (MetaWindow  *window,          }      } +  /* Delay showing the tile preview slightly to make it more unlikely to +   * trigger it unwittingly, e.g. when shaking loose the window or moving +   * it to another monitor. +   */ +  meta_screen_tile_preview_update (window->screen, +                                   window->tile_mode != META_TILE_NONE); +    if (display->grab_wireframe_active)      old = display->grab_wireframe_rect;    else      meta_window_get_client_root_coords (window, &old); -  /* Don't allow movement in the maximized directions */ -  if (window->maximized_horizontally) +  /* Don't allow movement in the maximized directions or while tiled */ +  if (window->maximized_horizontally || META_WINDOW_TILED (window))      new_x = old.x;    if (window->maximized_vertically)      new_y = old.y; @@ -7417,7 +7525,9 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,          {            if (meta_grab_op_is_moving (window->display->grab_op))              { -              if (event->xbutton.root == window->screen->xroot) +              if (window->tile_mode != META_TILE_NONE) +                meta_window_tile (window); +              else if (event->xbutton.root == window->screen->xroot)                  update_move (window, event->xbutton.state & ShiftMask,                               event->xbutton.x_root, event->xbutton.y_root);              } @@ -7575,6 +7685,29 @@ meta_window_get_work_area_all_xineramas (MetaWindow    *window,                window->desc, area->x, area->y, area->width, area->height);  } +void +meta_window_get_current_tile_area (MetaWindow    *window, +                                   MetaRectangle *tile_area) +{ +  const MetaXineramaScreenInfo *monitor; + +  g_return_if_fail (window->tile_mode != META_TILE_NONE); + +  /* The definition of "current" of meta_window_get_work_area_current_xinerama() +   * and meta_screen_get_current_xinerama() is slightly different: the former +   * refers to the monitor which contains the largest part of the window, the +   * latter to the one where the pointer is located. +   */ +  monitor = meta_screen_get_current_xinerama (window->screen); +  meta_window_get_work_area_for_xinerama (window, monitor->number, tile_area); + +  if (window->tile_mode == META_TILE_LEFT  || +      window->tile_mode == META_TILE_RIGHT) +    tile_area->width /= 2; + +  if (window->tile_mode == META_TILE_RIGHT) +    tile_area->x += tile_area->width; +}  gboolean  meta_window_same_application (MetaWindow *window, diff --git a/src/include/core.h b/src/include/core.h index 66db2f81..14c1c151 100644 --- a/src/include/core.h +++ b/src/include/core.h @@ -116,6 +116,10 @@ void meta_core_user_focus   (Display *xdisplay,                               Window   frame_xwindow,                               guint32  timestamp); +void meta_core_lower_beneath_focus_window (Display *xdisplay, +                                           Window   xwindow, +                                           guint32  timestamp); +  void meta_core_minimize         (Display *xdisplay,                                   Window   frame_xwindow);  void meta_core_toggle_maximize  (Display *xdisplay, diff --git a/src/include/prefs.h b/src/include/prefs.h index 2b7cfe41..4856d580 100644 --- a/src/include/prefs.h +++ b/src/include/prefs.h @@ -63,6 +63,7 @@ typedef enum    META_PREF_COMPOSITING_FAST_ALT_TAB,    META_PREF_RESIZE_WITH_RIGHT_BUTTON,    META_PREF_CENTER_NEW_WINDOWS, +  META_PREF_SIDE_BY_SIDE_TILING,    META_PREF_FORCE_FULLSCREEN  } MetaPreference; @@ -95,6 +96,7 @@ MetaWrapStyle               meta_prefs_get_wrap_style         (void);  gboolean                    meta_prefs_get_reduced_resources  (void);  gboolean                    meta_prefs_get_mate_accessibility (void);  gboolean                    meta_prefs_get_mate_animations   (void); +gboolean                    meta_prefs_get_side_by_side_tiling (void);  const char*                 meta_prefs_get_command            (int i); diff --git a/src/include/tile-preview.h b/src/include/tile-preview.h new file mode 100644 index 00000000..b0ca3b01 --- /dev/null +++ b/src/include/tile-preview.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* Meta tile preview */ + +/* + * Copyright (C) 2010 Florian Müllner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef META_TILE_PREVIEW_H +#define META_TILE_PREVIEW_H + +#include "boxes.h" + +typedef struct _MetaTilePreview MetaTilePreview; + +MetaTilePreview   *meta_tile_preview_new    (int                screen_number, +                                             gboolean           composited); +void               meta_tile_preview_free   (MetaTilePreview   *preview); +void               meta_tile_preview_show   (MetaTilePreview   *preview, +                                             MetaRectangle     *rect); +void               meta_tile_preview_hide   (MetaTilePreview   *preview); + +#endif /* META_TILE_PREVIEW_H */
\ No newline at end of file diff --git a/src/include/ui.h b/src/include/ui.h index a8866498..23525193 100644 --- a/src/include/ui.h +++ b/src/include/ui.h @@ -205,5 +205,6 @@ MetaUIDirection meta_ui_get_direction (void);  GdkPixbuf *meta_ui_get_pixbuf_from_pixmap (Pixmap   pmap);  #include "tabpopup.h" +#include "tile-preview.h"  #endif diff --git a/src/org.mate.marco.gschema.xml b/src/org.mate.marco.gschema.xml index 464deb34..23b10dee 100644 --- a/src/org.mate.marco.gschema.xml +++ b/src/org.mate.marco.gschema.xml @@ -166,6 +166,11 @@        <summary>Determine if new windows are created on the center of the screen</summary>        <description>By default, marco open new windows on the top left of the screen. If this option is enabled, new windows are open on the center of the screen, instead.</description>      </key> +    <key name="side-by-side-tiling" type="b"> +      <default>false</default> +      <summary>Whether to enable side-by-side tiling</summary> +      <description>If enabled, dropping windows on screen edges maximizes them vertically and resizes them horizontally to cover half of the available area. Drag-dropping to the top maximizes the window.</description> +    </key>    </schema>    <schema id="org.mate.Marco.workspace-names" path="/org/mate/marco/workspace-names/"> diff --git a/src/ui/tile-preview.c b/src/ui/tile-preview.c new file mode 100644 index 00000000..e782e6a5 --- /dev/null +++ b/src/ui/tile-preview.c @@ -0,0 +1,251 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* Mutter tile-preview marks the area a window will *ehm* snap to */ + +/* + * Copyright (C) 2010 Florian Müllner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <config.h> + +#include <gtk/gtk.h> +#include <cairo.h> + +#include "tile-preview.h" +#include "core.h" + +#define OUTLINE_WIDTH 5  /* frame width in non-composite case */ + + +struct _MetaTilePreview { +  GtkWidget     *preview_window; + +  GdkColor      *preview_color; +  guchar         preview_alpha; + +  MetaRectangle  tile_rect; + +  gboolean       has_alpha: 1; +}; + +static gboolean +meta_tile_preview_expose (GtkWidget      *widget, +                          GdkEventExpose *event, +                          gpointer        user_data) +{ +  MetaTilePreview *preview = user_data; +  GdkWindow *window; +  cairo_t *cr; + +  window = gtk_widget_get_window (widget); +  cr = gdk_cairo_create (window); + +  cairo_set_line_width (cr, 1.0); + +  if (preview->has_alpha) +    { + +      /* Fill the preview area with a transparent color */ +      cairo_set_source_rgba (cr, +                             (double)preview->preview_color->red   / 0xFFFF, +                             (double)preview->preview_color->green / 0xFFFF, +                             (double)preview->preview_color->blue  / 0xFFFF, +                             (double)preview->preview_alpha / 0xFF); + +      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); +      cairo_paint (cr); + +      /* Use the opaque color for the border */ +      gdk_cairo_set_source_color (cr, preview->preview_color); +    } +  else +    { +      GtkStyle *style = gtk_widget_get_style (preview->preview_window); + +      gdk_cairo_set_source_color (cr, &style->white); + +      cairo_rectangle (cr, +                       OUTLINE_WIDTH - 0.5, OUTLINE_WIDTH - 0.5, +                       preview->tile_rect.width - 2 * (OUTLINE_WIDTH - 1) - 1, +                       preview->tile_rect.height - 2 * (OUTLINE_WIDTH - 1) - 1); +      cairo_stroke (cr); +    } + +  cairo_rectangle (cr, +                   0.5, 0.5, +                   preview->tile_rect.width - 1, +                   preview->tile_rect.height - 1); +  cairo_stroke (cr); + +  cairo_destroy (cr); + +  return FALSE; +} + +static void +on_preview_window_style_set (GtkWidget *widget, +                             GtkStyle  *previous, +                             gpointer   user_data) +{ +  MetaTilePreview *preview = user_data; +  GtkStyle *style; + +  style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget), +                                     "GtkWindow.GtkIconView", +                                     "GtkWindow.GtkIconView", +                                     GTK_TYPE_ICON_VIEW); + +  if (style != NULL) +    g_object_ref (style); +  else +    style = gtk_style_new (); + +  gtk_style_get (style, GTK_TYPE_ICON_VIEW, +                 "selection-box-color", &preview->preview_color, +                 "selection-box-alpha", &preview->preview_alpha, +                 NULL); +  if (!preview->preview_color) +    { +      GdkColor selection = style->base[GTK_STATE_SELECTED]; +      preview->preview_color = gdk_color_copy (&selection); +    } + +  g_object_unref (style); +} + +MetaTilePreview * +meta_tile_preview_new (int      screen_number, +                       gboolean composited) +{ +  MetaTilePreview *preview; +  GdkColormap *rgba_colormap; +  GdkScreen *screen; + +  screen = gdk_display_get_screen (gdk_display_get_default (), screen_number); +  rgba_colormap = gdk_screen_get_rgba_colormap (screen); + +  preview = g_new (MetaTilePreview, 1); + +  preview->preview_window = gtk_window_new (GTK_WINDOW_POPUP); + +  gtk_window_set_screen (GTK_WINDOW (preview->preview_window), screen); +  gtk_widget_set_app_paintable (preview->preview_window, TRUE); + +  preview->preview_color = NULL; +  preview->preview_alpha = 0xFF; + +  preview->tile_rect.x = preview->tile_rect.y = 0; +  preview->tile_rect.width = preview->tile_rect.height = 0; + +  preview->has_alpha = rgba_colormap && composited; + +  if (preview->has_alpha) +    { +      gtk_widget_set_colormap (preview->preview_window, rgba_colormap); + +      g_signal_connect (preview->preview_window, "style-set", +                        G_CALLBACK (on_preview_window_style_set), preview); +    } + +  gtk_widget_realize (preview->preview_window); +  gdk_window_set_back_pixmap (gtk_widget_get_window (preview->preview_window), +                              NULL, FALSE); + +  g_signal_connect (preview->preview_window, "expose-event", +                    G_CALLBACK (meta_tile_preview_expose), preview); + +  return preview; +} + +void +meta_tile_preview_free (MetaTilePreview *preview) +{ +  gtk_widget_destroy (preview->preview_window); + +  if (preview->preview_color) +    gdk_color_free (preview->preview_color); + +  g_free (preview); +} + +void +meta_tile_preview_show (MetaTilePreview *preview, +                        MetaRectangle   *tile_rect) +{ +  GdkWindow *window; +  GdkRectangle old_rect; + +  if (gtk_widget_get_visible (preview->preview_window) +      && preview->tile_rect.x == tile_rect->x +      && preview->tile_rect.y == tile_rect->y +      && preview->tile_rect.width == tile_rect->width +      && preview->tile_rect.height == tile_rect->height) +    return; /* nothing to do */ + +  gtk_widget_show (preview->preview_window); +  window = gtk_widget_get_window (preview->preview_window); +  meta_core_lower_beneath_focus_window (gdk_display, +                                        GDK_WINDOW_XWINDOW (window), +                                        gtk_get_current_event_time ()); + +  old_rect.x = old_rect.y = 0; +  old_rect.width = preview->tile_rect.width; +  old_rect.height = preview->tile_rect.height; + +  gdk_window_invalidate_rect (window, &old_rect, FALSE); + +  preview->tile_rect = *tile_rect; + +  gdk_window_move_resize (window, +                          preview->tile_rect.x, preview->tile_rect.y, +                          preview->tile_rect.width, preview->tile_rect.height); + +  if (!preview->has_alpha) +    { +      GdkRectangle outer_rect, inner_rect; +      GdkRegion *outer_region, *inner_region; +      GdkColor black; + +      black = gtk_widget_get_style (preview->preview_window)->black; +      gdk_window_set_background (window, &black); + +      outer_rect.x = outer_rect.y = 0; +      outer_rect.width = preview->tile_rect.width; +      outer_rect.height = preview->tile_rect.height; + +      inner_rect.x = OUTLINE_WIDTH; +      inner_rect.y = OUTLINE_WIDTH; +      inner_rect.width = outer_rect.width - 2 * OUTLINE_WIDTH; +      inner_rect.height = outer_rect.height - 2 * OUTLINE_WIDTH; + +      outer_region = gdk_region_rectangle (&outer_rect); +      inner_region = gdk_region_rectangle (&inner_rect); + +      gdk_region_subtract (outer_region, inner_region); +      gdk_region_destroy (inner_region); + +      gdk_window_shape_combine_region (window, outer_region, 0, 0); +      gdk_region_destroy (outer_region); +    } +} + +void +meta_tile_preview_hide (MetaTilePreview *preview) +{ +  gtk_widget_hide (preview->preview_window); +}
\ No newline at end of file | 
