aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-08-02 20:10:35 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-08-02 20:10:35 +0200
commitae3225a4e7ef86d159fdf27834c453ffcd4da76c (patch)
treef52129b946fdebfee8ffd88bc2b90347786e6e0e
parent288f4c294ac1be89b151a3f96eb9d5cb9d91055f (diff)
downloadEpisodeBrowser-ae3225a4e7ef86d159fdf27834c453ffcd4da76c.tar.gz
Implement draggable split.
Next step is to allow a double click to reset the split to be automatically resized.
-rw-r--r--Makefile3
-rw-r--r--c/common.cpp14
-rw-r--r--c/common.h5
-rw-r--r--c/datalistview.cpp12
-rw-r--r--c/datalistview.h4
-rw-r--r--c/layout.cpp64
-rw-r--r--c/layout.h54
-rw-r--r--c/listview.cpp16
-rw-r--r--c/listview.h2
-rw-r--r--c/main.cpp64
-rw-r--r--c/main.h6
11 files changed, 189 insertions, 55 deletions
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<SM_CXVSCROLL>-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 <windows.h>
+#include <uxtheme.h>
+
+#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 <windows.h>
+
+#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 <uxtheme.h>
#include <SWI-Prolog.h>
+#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