Skip to content

Commit

Permalink
tabbar rearrangement: tabs can be reordered interactively via drag-an…
Browse files Browse the repository at this point in the history
…d-drop (mintty#1283)
  • Loading branch information
mintty committed Sep 21, 2024
1 parent ea0961a commit 5f440c6
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 18 deletions.
3 changes: 3 additions & 0 deletions docs/mintty.1
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,9 @@ via extended context menu or title bar menu.)
It is recommended to also set \fBSessionGeomSync=3\fP or higher to
achieve a tabbed window behaviour.

The tabbar can be reordered with user-definable functions tab\-(left|right)
or interactively via drag-and-drop on the tabs.

.SS Horizontal scrolling of terminal view and horizontal scrollbar

Mintty provides an optional horizontal scrollbar, which can be enabled
Expand Down
63 changes: 48 additions & 15 deletions src/winmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,8 +972,8 @@ update_tab_titles()
}
}

void
win_tab_left(void)
static bool
win_tabinfo_left(void)
{
for (int w = ntabinfo - 1; w > 0; w--)
if (tabinfo[w].wnd == wnd) {
Expand All @@ -991,20 +991,16 @@ win_tab_left(void)
SetWindowLong(wnd, GWL_USERDATA, __ud0);
SetWindowLong(wnd1, GWL_USERDATA, __ud1);

// update tabbar of current window
// refresh every time to make win_tab_move(n) work
refresh_tabinfo(false);
win_update_tabbar();
break;
}

// update tabbar of other windows of tabset
for (int w = 0; w < ntabinfo; w++)
if (tabinfo[w].wnd != wnd)
PostMessage(tabinfo[w].wnd, WM_USER, 0, WIN_TITLE);
return true;
}
return false;
}

void
win_tab_right(void)
static bool
win_tabinfo_right(void)
{
for (int w = 0; w < ntabinfo - 1; w++)
if (tabinfo[w].wnd == wnd) {
Expand All @@ -1022,18 +1018,55 @@ win_tab_right(void)
SetWindowLong(wnd, GWL_USERDATA, __ud0);
SetWindowLong(wnd1, GWL_USERDATA, __ud1);

// update tabbar of current window
refresh_tabinfo(false);
win_update_tabbar();
break;
return true;
}
return false;
}

static void
win_update_tabset()
{
// update tabbar of current window
//refresh_tabinfo(false); // already done in win_tabinfo_*
win_update_tabbar();

// update tabbar of other windows of tabset
for (int w = 0; w < ntabinfo; w++)
if (tabinfo[w].wnd != wnd)
PostMessage(tabinfo[w].wnd, WM_USER, 0, WIN_TITLE);
}

void
win_tab_left(void)
{
if (win_tabinfo_left())
win_update_tabset();
}

void
win_tab_right(void)
{
if (win_tabinfo_right())
win_update_tabset();
}

void
win_tab_move(int n)
{
bool moved = false;
while (n < 0) {
moved |= win_tabinfo_left();
n ++;
}
while (n > 0) {
moved |= win_tabinfo_right();
n --;
}
if (moved)
win_update_tabset();
}


/*
Window system colour configuration.
Expand Down
1 change: 1 addition & 0 deletions src/winpriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern void clear_tabs(void);
extern void add_tab(uint tabi, HWND wndi);
extern void win_tab_left(void);
extern void win_tab_right(void);
extern void win_tab_move(int n);
// Inter-window actions
enum {
WIN_MINIMIZE = 0,
Expand Down
106 changes: 103 additions & 3 deletions src/wintab.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// visual tabbar implementation (part of mintty)
// initially provided 2020 by Xiaohui Duan (#944)

#include "winpriv.h"
#include "wintab.h"
#if CYGWIN_VERSION_API_MINOR < 74
Expand All @@ -17,6 +20,8 @@ static bool initialized = false;
static const int max_tab_width = 300;
static const int min_tab_width = 20;
static int prev_tab_width = 0;
static int curr_tab_width;
static int xoff;

#define TABFONTSCALE 9/10

Expand Down Expand Up @@ -67,6 +72,7 @@ tabbar_update()
int tab_width = (win_width - 2 * tab_height) / ntabinfo;
tab_width = min(tab_width, max_tab_width);
tab_width = max(tab_width, min_tab_width);
curr_tab_width = tab_width;
//printf("width: %d %d %d\n", win_width, tab_width, ntabinfo);
SendMessage(tab_wnd, TCM_SETITEMSIZE, 0, tab_width | tab_height << 16);
TCITEMW tie;
Expand Down Expand Up @@ -155,7 +161,89 @@ static LRESULT CALLBACK
container_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
//printf("tabbar con_proc %03X\n", msg);
if (msg == WM_NOTIFY) {
static WORD dragmsg = 0;
static int dragidx = -1;
static int targidx = -1;

if (msg == WM_MOUSEACTIVATE) {
//printf("WM_MOUSEACTIVATE lo %02X hi %02X\n", LOWORD(lp), HIWORD(lp));
if (LOWORD(lp) == HTCLIENT && HIWORD(lp) == WM_LBUTTONDOWN) {
// begin drag-and-drop tab reordering
#ifdef determine_tab_index_by_wnd_in_item
// get tab wnd from TCITEM lParam, lookup tab index (not implemented)
int isel = SendMessage(tab_wnd, TCM_GETCURSEL, 0, 0);
TCITEMW tie;
tie.mask = TCIF_PARAM;
SendMessage(tab_wnd, TCM_GETITEM, isel, (LPARAM)&tie);
//dragidx = index_in_tabinfo(tie.lParam);
for (int i = 0; i < ntabinfo; i ++)
if (tabinfo[i].wnd == (HWND)tie.lParam) {
dragidx = i;
break;
}
//printf("i %d lp %p drag %d\n", isel, (void*)tie.lParam, dragidx);
#endif
// enquire cursor position; derive click-and-drag item index
POINT p;
if (GetCursorPos(&p) && ScreenToClient(hwnd, &p)) {
int x = p.x - xoff;
dragidx = x / curr_tab_width;
if (dragidx < ntabinfo)
dragmsg = HIWORD(lp);
else
dragidx = -1;
//printf("%d:%d (pw %d) x %d drag %d\n", (int)p.y, (int)p.x, curr_tab_width, x, dragidx);
}
}
}
else if (msg == WM_SETCURSOR && dragmsg && LOWORD(lp) == HTCLIENT) {
//printf("WM_SETCURSOR lo %02X hi %02X\n", LOWORD(lp), HIWORD(lp));
if (HIWORD(lp) == WM_MOUSEMOVE) {
// drag during tab reordering; display visual feedback

// cannot determine current cursor target tab via TCITEM
// (like #ifdef determine_tab_index_by_wnd_in_item above)
// as the index remains the click-and-drag tab

// enquire cursor position; derive drop target item index
POINT p;
if (GetCursorPos(&p) && ScreenToClient(hwnd, &p)) {
int x = p.x - xoff;
int dropidx = x / curr_tab_width;
//printf("%d:%d (pw %d) x %d drop %d\n", (int)p.y, (int)p.x, curr_tab_width, x, dropidx);
if (dropidx != targidx && targidx >= 0) {
// clear hover highlighting of previous drop target

targidx = -1;
}
if (dropidx != dragidx) {
// visual indication of tab dragging: hover highlighting

targidx = dropidx;
}
}
}
else if (HIWORD(lp) == WM_LBUTTONUP) {
// drop tab while dragging for tab reordering

// cannot determine current cursor target tab via TCITEM
// (like #ifdef determine_tab_index_by_wnd_in_item above)
// as the index remains the click-and-drag tab

// enquire cursor position; derive drop target item index
POINT p;
if (GetCursorPos(&p) && ScreenToClient(hwnd, &p)) {
int x = p.x - xoff;
int dropidx = x / curr_tab_width;
//printf("%d:%d (pw %d) x %d: drag %d -> drop %d\n", (int)p.y, (int)p.x, curr_tab_width, x, dragidx, dropidx);

// act on drop target item
win_tab_move(dropidx - dragidx);
dragmsg = 0;
}
}
}
else if (msg == WM_NOTIFY) {
//printf("tabbar con_proc WM_NOTIFY\n");
LPNMHDR lpnmhdr = (LPNMHDR)lp;
//printf("notify %lld %d %d\n", lpnmhdr->idFrom, lpnmhdr->code, TCN_SELCHANGE);
Expand All @@ -165,8 +253,8 @@ container_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
tie.mask = TCIF_PARAM;
SendMessage(tab_wnd, TCM_GETITEM, isel, (LPARAM)&tie);
//printf("%p\n", (void*)tie.lParam);
RECT rect_me;
GetWindowRect(wnd, &rect_me);
//RECT rect_me;
//GetWindowRect(wnd, &rect_me);
//printf("%d %d %d %d\n", rect_me.left, rect_me.right, rect_me.top, rect_me.bottom);
//ShowWindow((HWND)tie.lParam, SW_RESTORE);
//ShowWindow((HWND)tie.lParam, SW_SHOW);
Expand All @@ -187,6 +275,12 @@ container_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
SendMessage(tab_wnd, TCM_SETCURSEL, i, 0);
}
}
else if (lpnmhdr->code == (uint)NM_CLICK) {
// clear tab dragging; the tab drop could be hooked here
// but would not work if dropped in free space right of tabset
dragmsg = 0;
dragidx = -1;
}
}
else if (msg == WM_CREATE) {
//printf("tabbar con_proc WM_CREATE\n");
Expand Down Expand Up @@ -289,6 +383,12 @@ tabbar_init()
bar_wnd = CreateWindowExA(WS_EX_STATICEDGE, TABBARCLASS, "",
WS_CHILD | WS_BORDER,
0, 0, 0, 0, wnd, 0, inst, NULL);
// determine tab margin/offset for tab position/index calculation
RECT wr;
GetWindowRect(tab_wnd, &wr);
xoff = wr.left;
GetWindowRect(bar_wnd, &wr);
xoff -= wr.left;

initialized = true;
}
Expand Down
3 changes: 3 additions & 0 deletions wiki/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Window handling
* Tabs can be reordered interactively via drag-and-drop (#1283).

### 3.7.5 (17 September 2024) ###

Unicode and Emoji data
Expand Down

0 comments on commit 5f440c6

Please sign in to comment.