diff --git a/bin/dwn b/bin/dwn
index 9f55182..538af44 100755
Binary files a/bin/dwn and b/bin/dwn differ
diff --git a/build/ai.o b/build/ai.o
index e1eb3b9..b4913ff 100644
Binary files a/build/ai.o and b/build/ai.o differ
diff --git a/build/atoms.o b/build/atoms.o
index b600c15..de9a0fa 100644
Binary files a/build/atoms.o and b/build/atoms.o differ
diff --git a/build/client.d b/build/client.d
index 5c1a493..1e7328b 100644
--- a/build/client.d
+++ b/build/client.d
@@ -18,7 +18,8 @@ build/client.o: src/client.c include/client.h include/dwn.h \
/usr/include/dbus-1.0/dbus/dbus-server.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
- /usr/include/dbus-1.0/dbus/dbus-threads.h include/layout.h
+ /usr/include/dbus-1.0/dbus/dbus-threads.h include/layout.h \
+ include/panel.h
include/client.h:
include/dwn.h:
include/atoms.h:
@@ -46,3 +47,4 @@ include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/layout.h:
+include/panel.h:
diff --git a/build/client.o b/build/client.o
index a1527ff..ecedce5 100644
Binary files a/build/client.o and b/build/client.o differ
diff --git a/build/keys.d b/build/keys.d
index 94fa0ec..34e8dd7 100644
--- a/build/keys.d
+++ b/build/keys.d
@@ -18,7 +18,8 @@ build/keys.o: src/keys.c include/keys.h include/dwn.h include/client.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/news.h \
- include/applauncher.h include/decorations.h include/demo.h
+ include/applauncher.h include/decorations.h include/demo.h \
+ include/layout.h
include/keys.h:
include/dwn.h:
include/client.h:
@@ -49,3 +50,4 @@ include/news.h:
include/applauncher.h:
include/decorations.h:
include/demo.h:
+include/layout.h:
diff --git a/build/keys.o b/build/keys.o
index 2b7143a..8f1481e 100644
Binary files a/build/keys.o and b/build/keys.o differ
diff --git a/build/layout.o b/build/layout.o
index b04a777..66377f5 100644
Binary files a/build/layout.o and b/build/layout.o differ
diff --git a/build/panel.o b/build/panel.o
index dcd7b5c..3dfc344 100644
Binary files a/build/panel.o and b/build/panel.o differ
diff --git a/build/workspace.o b/build/workspace.o
index b69a31a..f850750 100644
Binary files a/build/workspace.o and b/build/workspace.o differ
diff --git a/include/dwn.h b/include/dwn.h
index df7983e..f170eb7 100644
--- a/include/dwn.h
+++ b/include/dwn.h
@@ -62,6 +62,26 @@ typedef enum {
CLIENT_MAXIMIZED = (1 << 5)
} ClientFlags;
+typedef enum {
+ SNAP_H_NONE = 0,
+ SNAP_H_LEFT,
+ SNAP_H_RIGHT,
+ SNAP_H_FULL
+} SnapHorizontal;
+
+typedef enum {
+ SNAP_V_NONE = 0,
+ SNAP_V_TOP,
+ SNAP_V_BOTTOM,
+ SNAP_V_FULL
+} SnapVertical;
+
+typedef struct {
+ SnapHorizontal horizontal;
+ SnapVertical vertical;
+ long timestamp;
+} SnapConstraint;
+
typedef struct Client Client;
typedef struct Workspace Workspace;
typedef struct Monitor Monitor;
@@ -80,6 +100,7 @@ struct Client {
unsigned int workspace;
char title[256];
char class[64];
+ SnapConstraint snap;
Client *next;
Client *prev;
Client *mru_next;
diff --git a/include/keys.h b/include/keys.h
index cba08ee..ec507f2 100644
--- a/include/keys.h
+++ b/include/keys.h
@@ -83,6 +83,8 @@ void key_show_shortcuts(void);
void key_start_tutorial(void);
void key_snap_left(void);
void key_snap_right(void);
+void key_snap_up(void);
+void key_snap_down(void);
void key_start_demo(void);
void tutorial_start(void);
diff --git a/include/layout.h b/include/layout.h
index ca807c1..a83c881 100644
--- a/include/layout.h
+++ b/include/layout.h
@@ -17,6 +17,9 @@ void layout_arrange_monocle(int workspace);
int layout_get_usable_area(int *x, int *y, int *width, int *height);
int layout_count_tiled_clients(int workspace);
+void layout_apply_snap_constraint(Client *c, int area_x, int area_y,
+ int area_w, int area_h, int gap);
+
const char *layout_get_name(LayoutType layout);
const char *layout_get_symbol(LayoutType layout);
diff --git a/site/shortcuts.html b/site/shortcuts.html
index 73a11a9..5845e9e 100644
--- a/site/shortcuts.html
+++ b/site/shortcuts.html
@@ -250,13 +250,37 @@
Super + D |
Decrease master window count |
+
+
+
+ Window Snapping (Composable)
+
+ Snapping shortcuts are composable: press Super+Left then Super+Up for top-left quarter. Press the same key twice to expand to full in that axis.
+
+
+
+
+
+ | Shortcut |
+ Action |
+
+
+
| Super + Left |
- Snap window to left half (50% width) |
+ Snap left 50% (press twice for full width) |
| Super + Right |
- Snap window to right half (50% width) |
+ Snap right 50% (press twice for full width) |
+
+
+ | Super + Up |
+ Snap top 50% (press twice for full height) |
+
+
+ | Super + Down |
+ Snap bottom 50% (press twice for full height) |
@@ -291,7 +315,7 @@
News Ticker
- Navigate the scrolling news ticker displayed in the bottom panel.
+ The scrolling news ticker is displayed in the bottom panel.
@@ -302,14 +326,6 @@
-
- | Super + Down |
- Next news article |
-
-
- | Super + Up |
- Previous news article |
-
| Super + Return |
Open current article in browser |
@@ -380,7 +396,7 @@
Super+H/L Resize master
- Super+Left/Right Snap 50%
+ Super+Arrows Composable snap
Shift+F1-9 Move to workspace
diff --git a/src/client.c b/src/client.c
index fde5c0d..d9a6088 100644
--- a/src/client.c
+++ b/src/client.c
@@ -12,6 +12,7 @@
#include "decorations.h"
#include "notifications.h"
#include "layout.h"
+#include "panel.h"
#include
#include
@@ -46,6 +47,9 @@ Client *client_create(Window window)
client->prev = NULL;
client->mru_next = NULL;
client->mru_prev = NULL;
+ client->snap.horizontal = SNAP_H_NONE;
+ client->snap.vertical = SNAP_V_NONE;
+ client->snap.timestamp = 0;
XWindowAttributes wa;
int orig_width = 640, orig_height = 480;
@@ -396,6 +400,7 @@ void client_raise(Client *client)
XRaiseWindow(dwn->display, win);
notifications_raise_all();
+ panel_raise_all();
}
void client_lower(Client *client)
@@ -443,6 +448,13 @@ void client_move(Client *client, int x, int y)
return;
}
+ int area_x, area_y, area_w, area_h;
+ layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
+
+ if (y < area_y) {
+ y = area_y;
+ }
+
client->x = x;
client->y = y;
client_configure(client);
diff --git a/src/keys.c b/src/keys.c
index 6fcd568..0d39f7b 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -15,6 +15,7 @@
#include "applauncher.h"
#include "decorations.h"
#include "demo.h"
+#include "layout.h"
#include
#include
@@ -530,9 +531,9 @@ void keys_setup_defaults(void)
keys_bind(MOD_SUPER, XK_t, key_start_tutorial, "Start tutorial");
- keys_bind(MOD_SUPER, XK_Down, key_news_next, "Next news article");
+ keys_bind(MOD_SUPER, XK_Down, key_snap_down, "Snap window bottom/full");
- keys_bind(MOD_SUPER, XK_Up, key_news_prev, "Previous news article");
+ keys_bind(MOD_SUPER, XK_Up, key_snap_up, "Snap window top/full");
keys_bind(MOD_SUPER, XK_Return, key_news_open, "Open news article");
@@ -802,8 +803,12 @@ void key_show_shortcuts(void)
"Super+L Expand master area\n"
"Super+I Add to master\n"
"Super+D Remove from master\n"
- "Super+Left Snap window left (50%)\n"
- "Super+Right Snap window right (50%)\n"
+ "\n"
+ "=== Window Snapping (composable) ===\n"
+ "Super+Left Left 50% (2x=full width)\n"
+ "Super+Right Right 50% (2x=full width)\n"
+ "Super+Up Top 50% (2x=full height)\n"
+ "Super+Down Bottom 50% (2x=full height)\n"
"\n"
"=== AI Features ===\n"
"Super+A Show AI context\n"
@@ -851,10 +856,6 @@ void key_screenshot(void)
void key_snap_left(void)
{
- if (dwn == NULL) {
- return;
- }
-
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->focused == NULL) {
return;
@@ -862,42 +863,30 @@ void key_snap_left(void)
Client *c = ws->focused;
- int work_x = 0;
- int work_y = 0;
- int work_width = dwn->screen_width;
- int work_height = dwn->screen_height;
-
- if (dwn->config != NULL) {
- if (dwn->config->top_panel_enabled) {
- work_y += config_get_panel_height();
- work_height -= config_get_panel_height();
- }
- if (dwn->config->bottom_panel_enabled) {
- work_height -= config_get_panel_height();
- }
- }
-
- int gap = config_get_gap();
-
if (!client_is_floating(c)) {
client_set_floating(c, true);
}
- c->x = work_x + gap;
- c->y = work_y + gap;
- c->width = (work_width / 2) - (gap * 2);
- c->height = work_height - (gap * 2);
+ if (c->snap.horizontal == SNAP_H_LEFT) {
+ c->snap.horizontal = SNAP_H_FULL;
+ } else {
+ c->snap.horizontal = SNAP_H_LEFT;
+ }
+ if (c->snap.vertical == SNAP_V_NONE) {
+ c->snap.vertical = SNAP_V_FULL;
+ }
+ int area_x, area_y, area_w, area_h;
+ layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
+ int gap = config_get_gap();
+
+ layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
client_configure(c);
decorations_render(c, true);
}
void key_snap_right(void)
{
- if (dwn == NULL) {
- return;
- }
-
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->focused == NULL) {
return;
@@ -905,32 +894,86 @@ void key_snap_right(void)
Client *c = ws->focused;
- int work_x = 0;
- int work_y = 0;
- int work_width = dwn->screen_width;
- int work_height = dwn->screen_height;
-
- if (dwn->config != NULL) {
- if (dwn->config->top_panel_enabled) {
- work_y += config_get_panel_height();
- work_height -= config_get_panel_height();
- }
- if (dwn->config->bottom_panel_enabled) {
- work_height -= config_get_panel_height();
- }
+ if (!client_is_floating(c)) {
+ client_set_floating(c, true);
}
+ if (c->snap.horizontal == SNAP_H_RIGHT) {
+ c->snap.horizontal = SNAP_H_FULL;
+ } else {
+ c->snap.horizontal = SNAP_H_RIGHT;
+ }
+ if (c->snap.vertical == SNAP_V_NONE) {
+ c->snap.vertical = SNAP_V_FULL;
+ }
+
+ int area_x, area_y, area_w, area_h;
+ layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
int gap = config_get_gap();
+ layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
+ client_configure(c);
+ decorations_render(c, true);
+}
+
+void key_snap_up(void)
+{
+ Workspace *ws = workspace_get_current();
+ if (ws == NULL || ws->focused == NULL) {
+ return;
+ }
+
+ Client *c = ws->focused;
+
if (!client_is_floating(c)) {
client_set_floating(c, true);
}
- c->x = work_x + (work_width / 2) + gap;
- c->y = work_y + gap;
- c->width = (work_width / 2) - (gap * 2);
- c->height = work_height - (gap * 2);
+ if (c->snap.vertical == SNAP_V_TOP) {
+ c->snap.vertical = SNAP_V_FULL;
+ } else {
+ c->snap.vertical = SNAP_V_TOP;
+ }
+ if (c->snap.horizontal == SNAP_H_NONE) {
+ c->snap.horizontal = SNAP_H_FULL;
+ }
+ int area_x, area_y, area_w, area_h;
+ layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
+ int gap = config_get_gap();
+
+ layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
+ client_configure(c);
+ decorations_render(c, true);
+}
+
+void key_snap_down(void)
+{
+ Workspace *ws = workspace_get_current();
+ if (ws == NULL || ws->focused == NULL) {
+ return;
+ }
+
+ Client *c = ws->focused;
+
+ if (!client_is_floating(c)) {
+ client_set_floating(c, true);
+ }
+
+ if (c->snap.vertical == SNAP_V_BOTTOM) {
+ c->snap.vertical = SNAP_V_FULL;
+ } else {
+ c->snap.vertical = SNAP_V_BOTTOM;
+ }
+ if (c->snap.horizontal == SNAP_H_NONE) {
+ c->snap.horizontal = SNAP_H_FULL;
+ }
+
+ int area_x, area_y, area_w, area_h;
+ layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
+ int gap = config_get_gap();
+
+ layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
client_configure(c);
decorations_render(c, true);
}
diff --git a/src/layout.c b/src/layout.c
index ff486db..5233cf3 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -229,6 +229,51 @@ int layout_count_tiled_clients(int workspace)
return count;
}
+void layout_apply_snap_constraint(Client *c, int area_x, int area_y,
+ int area_w, int area_h, int gap)
+{
+ if (c == NULL) {
+ return;
+ }
+
+ int half_w = area_w / 2;
+ int half_h = area_h / 2;
+
+ switch (c->snap.horizontal) {
+ case SNAP_H_LEFT:
+ c->x = area_x + gap;
+ c->width = half_w - gap * 2;
+ break;
+ case SNAP_H_RIGHT:
+ c->x = area_x + half_w + gap;
+ c->width = half_w - gap * 2;
+ break;
+ case SNAP_H_FULL:
+ c->x = area_x + gap;
+ c->width = area_w - gap * 2;
+ break;
+ case SNAP_H_NONE:
+ break;
+ }
+
+ switch (c->snap.vertical) {
+ case SNAP_V_TOP:
+ c->y = area_y + gap;
+ c->height = half_h - gap * 2;
+ break;
+ case SNAP_V_BOTTOM:
+ c->y = area_y + half_h + gap;
+ c->height = half_h - gap * 2;
+ break;
+ case SNAP_V_FULL:
+ c->y = area_y + gap;
+ c->height = area_h - gap * 2;
+ break;
+ case SNAP_V_NONE:
+ break;
+ }
+}
+
const char *layout_get_name(LayoutType layout)
{
if (layout >= 0 && layout < LAYOUT_COUNT) {