From ae3225a4e7ef86d159fdf27834c453ffcd4da76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Tue, 2 Aug 2022 20:10:35 +0200 Subject: Implement draggable split. Next step is to allow a double click to reset the split to be automatically resized. --- Makefile | 3 ++- c/common.cpp | 14 ++++++++++++ c/common.h | 5 +++-- c/datalistview.cpp | 12 +++++++++- c/datalistview.h | 4 ++++ c/layout.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ c/layout.h | 54 +++++++++++++++++++++++++++++++++++++++++++++ c/listview.cpp | 16 +++++++------- c/listview.h | 2 +- c/main.cpp | 64 ++++++++++++++++++++++++------------------------------ c/main.h | 6 ----- 11 files changed, 189 insertions(+), 55 deletions(-) create mode 100644 c/layout.cpp create mode 100644 c/layout.h delete mode 100644 c/main.h diff --git a/Makefile b/Makefile index 1f62c99..f837066 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ EXE = EpisodeBrowser.exe -OBJ = b/common.obj b/datalistview.obj b/debug.obj b/episodelistview.obj b/listview.obj b/pl.obj b/resource.obj +OBJ = b/common.obj b/datalistview.obj b/debug.obj b/episodelistview.obj +OBJ += b/layout.obj b/listview.obj b/pl.obj b/resource.obj PL = pl/cfg.pl pl/episode_data.pl pl/local_episodes.pl pl/track_episodes.pl CL = swipl-ld diff --git a/c/common.cpp b/c/common.cpp index 2e16c59..fcf1da2 100644 --- a/c/common.cpp +++ b/c/common.cpp @@ -148,3 +148,17 @@ int EBMessageBox(const wchar_t* const wszText, const wchar_t* const wszCaption, require(UnhookWindowsHookEx(hHook)); return r; } + +int GetRelativeCursorPos(HWND hWnd, POINT* pt) +{ + RECT rc; + if (!GetClientRect(hWnd, &rc)) return 0; + SetLastError(ERROR_SUCCESS); + if (!MapWindowPoints(hWnd, NULL, (POINT*)&rc, 2)) return 0; + + POINT ptMouse; + if (!GetCursorPos(&ptMouse)) return 0; + pt->x = ptMouse.x-rc.left; + pt->y = ptMouse.y-rc.top; + return 1; +} diff --git a/c/common.h b/c/common.h index 6dfd782..d908d12 100644 --- a/c/common.h +++ b/c/common.h @@ -11,6 +11,9 @@ #define WA "A" #endif +int EBMessageBox(const wchar_t* wszText, const wchar_t* wszCaption, unsigned uType); +int GetRelativeCursorPos(HWND hWnd, POINT* pt); + struct wstring_owner { static wstring_owner from_narrow(const char* buf, int cp = CP_UTF8); @@ -28,8 +31,6 @@ struct wstring_owner wchar_t* p = nullptr; }; -int EBMessageBox(const wchar_t* wszText, const wchar_t* wszCaption, unsigned uType); - struct Win32Error : public std::exception { Win32Error(); diff --git a/c/datalistview.cpp b/c/datalistview.cpp index dd59250..ecaa76f 100644 --- a/c/datalistview.cpp +++ b/c/datalistview.cpp @@ -9,7 +9,7 @@ #include "datalistview.h" #include "episodelistview.h" #include "listview.h" -#include "main.h" +#include "layout.h" #include "pl.h" DataListView::DataListView(const HWND hWndParent) @@ -30,6 +30,11 @@ DataListView::DataListView(const HWND hWndParent) ListView_InsertColumn(hWnd, DLVSIVALUE, &lvc); } +int DataListView::Height(int bHeader) +{ + return m_height? m_height: ListView::Height(bHeader); +} + void DataListView::ResizeColumns(int w) { ListView_SetColumnWidth(hWnd, DLVSIKEY, LVSCW_AUTOSIZE); @@ -38,6 +43,11 @@ void DataListView::ResizeColumns(int w) ListView_SetColumnWidth(hWnd, DLVSIVALUE, w-cxColumn-Metric-Dpi(4)); } +void DataListView::SetHeight(int h) +{ + m_height = h; +} + void DataListView::ShowEpisode(const int iEpisode) { extern EpisodeListView* const g_elv; diff --git a/c/datalistview.h b/c/datalistview.h index 5865ed1..c094d0e 100644 --- a/c/datalistview.h +++ b/c/datalistview.h @@ -9,8 +9,12 @@ struct DataListView : public ListView { DataListView(HWND hWndParent); + int Height(int bHeader = -1) override; void ResizeColumns(int w) override; + void SetHeight(int h); void ShowEpisode(int iEpisode); +private: + int m_height = 0; }; #endif diff --git a/c/layout.cpp b/c/layout.cpp new file mode 100644 index 0000000..77aa88e --- /dev/null +++ b/c/layout.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include "common.h" +#include "episodelistview.h" +#include "datalistview.h" +#include "layout.h" + +extern HWND g_hWnd; +extern HWND g_hWndStatus; +extern EpisodeListView* g_elv; +extern DataListView* g_dlv; + +void UpdateLayout(int w, int h) +{ + if (!g_hWndStatus) return; + + RECT rc, rrStatus; + if (w && h) rc = {0, 0, w, h}; + else require(GetClientRect(g_hWnd, &rc)); + require(GetRelativeRect(g_hWndStatus, &rrStatus)); + + SendMessage(g_hWnd, WM_SETREDRAW, FALSE, 0); + + /* Resize list views. */ + const long pad = IsThemeActive()? Dpi(6): 0; /* Add padding in modern themes. */ + const long cyDlv = rrStatus.top-g_dlv->Height()-pad; + require(SetWindowRect(g_dlv->hWnd, pad, cyDlv, rc.right-pad, rrStatus.top-pad)); + require(SetWindowRect(g_elv->hWnd, pad, pad, rc.right-pad, cyDlv-pad)); + g_dlv->ResizeColumns(rc.right-pad-pad); + g_elv->ResizeColumns(rc.right-pad-pad); + + /* Resize status bar parts. */ + const int aParts[] = {rc.right-Dpi(55), rc.right}; + SendMessage(g_hWndStatus, SB_SETPARTS, (WPARAM)sizeof(aParts), (LPARAM)aParts); + + SendMessage(g_hWnd, WM_SETREDRAW, TRUE, 0); + RedrawWindow(g_hWnd, NULL, NULL, + RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN); +} + +bool Dragger::InDragArea(const int x, const int y) +{ + RECT rrDlv; + require(GetRelativeRect(g_dlv->hWnd, &rrDlv)); + + const int pad = IsThemeActive()? Dpi(6): 0; + const int extra = IsThemeActive()? 0: Dpi(2); + if (x < rrDlv.left || x > rrDlv.right) return false; + if (y < rrDlv.top-pad*2-extra*3 || y > rrDlv.top+extra) return false; + return true; +} + +void Dragger::Drag(const int, const int y) +{ + RECT rrDlv; + require(GetRelativeRect(g_dlv->hWnd, &rrDlv)); + int h; + h = rrDlv.bottom-y; + g_dlv->SetHeight(h); + UpdateLayout(); + RedrawWindow(g_hWnd, NULL, NULL, + RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_UPDATENOW); +} diff --git a/c/layout.h b/c/layout.h new file mode 100644 index 0000000..82189fb --- /dev/null +++ b/c/layout.h @@ -0,0 +1,54 @@ +#ifndef LAYOUT_H +#define LAYOUT_H + +#include + +#include "common.h" + +void UpdateLayout(int w = 0, int h = 0); + +struct Dragger +{ + bool HandleDown(); + bool HandleMove(); +private: + bool InDragArea(int x, int y); + bool IsDown(); + void Drag(int x, int y); + bool m_bActive = false; +}; + +inline bool Dragger::HandleDown() +{ + extern HWND g_hWnd; + POINT pt; + require(GetRelativeCursorPos(g_hWnd, &pt)); + if (!InDragArea(pt.x, pt.y)) return false; + return m_bActive = true; +} + +inline bool Dragger::IsDown() +{ + return GetKeyState(VK_LBUTTON) & 0x8000; +} + +inline bool Dragger::HandleMove() +{ + extern HWND g_hWnd; + POINT pt; + require(GetRelativeCursorPos(g_hWnd, &pt)); + + extern HCURSOR g_hcSizeNs; + bool r = true; + if (InDragArea(pt.x, pt.y)) + SetCursor(g_hcSizeNs); + else + r = false; + if (m_bActive) + Drag(pt.x, pt.y); + if (!IsDown()) + m_bActive = false; + return r; +} + +#endif diff --git a/c/listview.cpp b/c/listview.cpp index 6dfa888..b10ae88 100644 --- a/c/listview.cpp +++ b/c/listview.cpp @@ -4,7 +4,7 @@ #include "common.h" #include "listview.h" -#include "main.h" +#include "layout.h" extern HFONT g_hfNormal; static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); @@ -25,7 +25,7 @@ ListView::ListView(const HWND hWndParent, const HMENU hMenu, const DWORD dwStyle m_proc0 = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)::WndProc); - ListView_SetExtendedListViewStyle(hWnd, LVS_EX_FULLROWSELECT); + ListView_SetExtendedListViewStyle(hWnd, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); SendMessage(hWnd, WM_SETFONT, (WPARAM)g_hfNormal, MAKELPARAM(FALSE, 0)); } @@ -50,18 +50,15 @@ void ListView::ResizeColumns(int) {} void ListView::UpdateTheme(const BOOL bThemeActive) { - DWORD dwStyle; const wchar_t* theme; WORD action; extern int g_bThemes; if (!g_bThemes) return; if (bThemeActive) { - dwStyle = LVS_EX_DOUBLEBUFFER; theme = L"Explorer"; action = UIS_SET; } else { - dwStyle = 0; theme = NULL; action = UIS_CLEAR; } @@ -69,9 +66,6 @@ void ListView::UpdateTheme(const BOOL bThemeActive) /* Use modern "Explorer" theme. */ SetWindowTheme(hWnd, theme, NULL); - /* The modern theme requires double buffering. */ - ListView_SetExtendedListViewStyleEx(hWnd, LVS_EX_DOUBLEBUFFER, dwStyle); - /* Hide focus rectangles. */ SendMessage(hWnd, WM_UPDATEUISTATE, MAKEWPARAM(action, UISF_HIDEFOCUS), 0); } @@ -79,6 +73,7 @@ void ListView::UpdateTheme(const BOOL bThemeActive) LRESULT CALLBACK ListView::WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { + extern Dragger g_dragDlv; switch (uMsg) { case WM_NOTIFY: switch (((NMHDR*)lParam)->code) { @@ -87,6 +82,11 @@ LRESULT CALLBACK ListView::WndProc(const HWND hWnd, const UINT uMsg, return TRUE; } break; + case WM_LBUTTONDOWN: + case WM_NCLBUTTONDOWN: + if (g_dragDlv.HandleDown()) + return 0; + break; } return CallWindowProc(m_proc0, hWnd, uMsg, wParam, lParam); diff --git a/c/listview.h b/c/listview.h index 84ad9d5..c812cd8 100644 --- a/c/listview.h +++ b/c/listview.h @@ -10,7 +10,7 @@ struct ListView ListView(HWND hWndParent, HMENU hMenu, DWORD dwStyle); bool FindNextItem(LVITEM* lvi, LPARAM lParam); - int Height(int bHeader = -1); + virtual int Height(int bHeader = -1); virtual void ResizeColumns(int w); virtual void UpdateTheme(BOOL bThemeActive); virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/c/main.cpp b/c/main.cpp index ebce3a6..477e750 100644 --- a/c/main.cpp +++ b/c/main.cpp @@ -4,17 +4,22 @@ #include #include +#include "debug.h" #include "resource.h" #include "common.h" #include "datalistview.h" #include "episodelistview.h" -#include "main.h" +#include "layout.h" #include "pl.h" /* Looked-up constants. */ int g_bThemes; int g_dpi = 96; +/* Cursors. */ +HCURSOR g_hcArrow = LoadCursor(NULL, IDC_ARROW); +HCURSOR g_hcSizeNs = LoadCursor(NULL, IDC_SIZENS); + /* Fonts. */ HFONT g_hfNormal; HFONT g_hfBold; @@ -31,6 +36,9 @@ HWND g_hWndStatus; DataListView* g_dlv; EpisodeListView* g_elv; +/* Layout handlers. */ +Dragger g_dragDlv; + /* View settings. */ int g_bViewWatched = 1; int g_bViewTVOriginal = 1; @@ -38,8 +46,8 @@ char g_currentScreenwriter[64]; static LRESULT CALLBACK CBTProc(int, WPARAM, LPARAM); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -void WndProcMainMenu(const HWND, unsigned short); -void WndProcContextMenu(const HWND, unsigned short); +static void HandleMainMenu(HWND, unsigned short); +static void HandleContextMenu(HWND, unsigned short); static void WaitFor(const char*, const char*); static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); static void UpdateTheme(); @@ -98,7 +106,7 @@ int WINAPI WinMain(const HINSTANCE hInstance, const HINSTANCE, char* const, cons wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hCursor = g_hcArrow; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); wc.lpszClassName = L"Episode Browser"; @@ -267,10 +275,10 @@ LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const unsigned short command = LOWORD(wParam); switch (ID_GROUP(command)) { case IDG_MENU: - WndProcMainMenu(hWnd, command); + HandleMainMenu(hWnd, command); break; case IDG_CTX: - WndProcContextMenu(hWnd, command); + HandleContextMenu(hWnd, command); break; } break; @@ -327,6 +335,18 @@ LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(0,0), (LPARAM)tip); break; } + case WM_LBUTTONDOWN: + g_dragDlv.HandleDown(); + break; + case WM_SETCURSOR: + if (!g_dragDlv.HandleMove()) { + /* Use default cursor. */ + if ((HWND)wParam == hWnd) + return DefWindowProc(hWnd, uMsg, wParam, lParam); + else + return 0; + } + return 1; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } @@ -335,7 +355,7 @@ LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, } /* Process main menu commands. */ -void WndProcMainMenu(const HWND hWnd, unsigned short command) +void HandleMainMenu(const HWND hWnd, unsigned short command) { switch (command) { case IDM_FILE_EXIT: @@ -402,7 +422,7 @@ void WndProcMainMenu(const HWND hWnd, unsigned short command) } /* Process context menu commands. */ -void WndProcContextMenu(const HWND, unsigned short command) +void HandleContextMenu(const HWND, unsigned short command) { int cNotFound = 0; @@ -528,34 +548,6 @@ INT_PTR CALLBACK AboutDlgProc(const HWND hWnd, const UINT uMsg, const WPARAM wPa return TRUE; } -void UpdateLayout(int w, int h) -{ - if (!g_hWndStatus) return; - - RECT rc, rrStatus; - if (w && h) rc = {0, 0, w, h}; - else require(GetClientRect(g_hWnd, &rc)); - require(GetRelativeRect(g_hWndStatus, &rrStatus)); - - SendMessage(g_hWnd, WM_SETREDRAW, FALSE, 0); - - /* Resize list views. */ - const long pad = IsThemeActive()? Dpi(6): 0; /* Add padding in modern themes. */ - const long cyDlv = rrStatus.top-g_dlv->Height()-pad; - require(SetWindowRect(g_dlv->hWnd, pad, cyDlv, rc.right-pad, rrStatus.top-pad)); - require(SetWindowRect(g_elv->hWnd, pad, pad, rc.right-pad, cyDlv-pad)); - g_dlv->ResizeColumns(rc.right-pad-pad); - g_elv->ResizeColumns(rc.right-pad-pad); - - /* Resize status bar parts. */ - const int aParts[] = {rc.right-Dpi(55), rc.right}; - SendMessage(g_hWndStatus, SB_SETPARTS, (WPARAM)sizeof(aParts), (LPARAM)aParts); - - SendMessage(g_hWnd, WM_SETREDRAW, TRUE, 0); - RedrawWindow(g_hWnd, NULL, NULL, - RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN); -} - /* Try to style application according to current Windows theme. */ void UpdateTheme() { diff --git a/c/main.h b/c/main.h deleted file mode 100644 index 1f9d3bf..0000000 --- a/c/main.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -void UpdateLayout(int w = 0, int h = 0); - -#endif -- cgit v1.2.3