Skip to content

Commit

Permalink
New feature: mouse cursors
Browse files Browse the repository at this point in the history
Widgets can set their own mouse cursors. When you hover over a widget the mouse cursor will change.
  • Loading branch information
diegoiast committed Jan 14, 2024
1 parent fcb1a34 commit 09c3946
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 62 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ set(SVISION_SOURCES
src/layout.h
src/listview.cpp
src/listview.h
src/mousecursors.h
src/platform.cpp
src/platform.h
src/radiobuttongroup.h
Expand Down
10 changes: 6 additions & 4 deletions src/main.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@
#include <spdlog/spdlog.h>
#include <timer.h>

auto handle_event(std::shared_ptr<PlatformWindow> w, const Event &ev);
auto handle_event(int window_id, int widget_id, const Event &ev);
// TODO future expansion, ideas
// auto handle_event(std::shared_ptr<PlatformWindow> w, const Event &ev);
// auto handle_event(int window_id, int widget_id, const Event &ev);

struct DebugWidget : public Widget {
bool state_pressed = false;
Position pos = {0, 0};
bool unclick_inside = false;

DebugWidget(Position position, Size size, uint32_t color) : Widget(position, size, color) {}
DebugWidget(Position position, Size size, uint32_t color) : Widget(position, size, color) {
this->mouse_cursor = MouseCursor::Cross;
}

auto on_hover(const EventMouse &event) -> void {
// spdlog::info("Widget: Mouse over: {}x{}", event.x, event.y);
pos.x = event.x;
pos.y = event.y;
invalidate();
Expand Down
30 changes: 30 additions & 0 deletions src/mousecursors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

enum class MouseCursor {
Inherit,
Arrow,
Wait,
Buzy,
Beam,
SizeVertical,
SizeHorizonal,
SizeDiagonalRight,
SizeDiagonalLeft,
SizeAll,
SplitHorizontal,
SplitVertical,
NoCursor,
Pointer,
Forbidden,
WhatsThis,
Cross,

Normal = Arrow,
Blank = NoCursor,
Link = Pointer,
Edit = Beam,
SizeNS = SizeVertical,
SizeWE = SizeHorizonal,
SizeNWSE = SizeDiagonalLeft,
SizeNESW = SizeDiagonalRight,
};
6 changes: 5 additions & 1 deletion src/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#pragma once

#include <mousecursors.h>

#include <list>
#include <memory>
#include <string>
Expand All @@ -27,7 +29,9 @@ struct Platform {
-> std::shared_ptr<PlatformWindow> = 0;
virtual auto show_window(std::shared_ptr<PlatformWindow> window) -> void = 0;

// TODO: I would like to pass the shared pointer, to keep API consistent
// TODO: Should I pass the shared pointer, to keep API consistent?
virtual auto clear_cursor_cache() -> void = 0;
virtual auto set_cursor(PlatformWindow &window, MouseCursor cursor) -> void = 0;
virtual auto invalidate(PlatformWindow &window) -> void = 0;
virtual auto main_loop() -> void = 0;
};
129 changes: 95 additions & 34 deletions src/platformwin32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
int argc = __argc;
char **argv = __argv;
int result = main(argc, argv);
LocalFree(argv);
return result;
}

std::wstring StringToWideString(const std::string &narrowStr) {
static std::wstring StringToWideString(const std::string &narrowStr) {
int wideStrLength = MultiByteToWideChar(CP_UTF8, 0, narrowStr.c_str(), -1, nullptr, 0);
if (wideStrLength == 0) {
return L"";
Expand All @@ -40,7 +39,7 @@ std::wstring StringToWideString(const std::string &narrowStr) {
return wideStr;
}

auto convert_win32_mouse_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventMouse {
static auto convert_win32_mouse_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventMouse {
auto event = EventMouse();
switch (msg) {
case WM_LBUTTONDOWN:
Expand Down Expand Up @@ -109,7 +108,7 @@ auto convert_win32_mouse_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventM

#include <platformwin32-keycodes.h>

auto convert_win32_keyboard_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventKeyboard {
static auto convert_win32_keyboard_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventKeyboard {
auto event = EventKeyboard();
event.modifiers = ((GetKeyState(VK_CONTROL) & 0x8000) >> 15) |
((GetKeyState(VK_SHIFT) & 0x8000) >> 14) |
Expand All @@ -129,7 +128,7 @@ auto convert_win32_keyboard_event(UINT msg, WPARAM wParam, LPARAM lParam) -> Eve
return event;
}

auto convert_win32_resize_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventResize {
static auto convert_win32_resize_event(UINT msg, WPARAM wParam, LPARAM lParam) -> EventResize {
auto event = EventResize();

switch (msg) {
Expand All @@ -155,6 +154,47 @@ auto convert_win32_resize_event(UINT msg, WPARAM wParam, LPARAM lParam) -> Event
return event;
}

static auto convert_mouse_cursor_to_win32(MouseCursor cursor) -> LPSTR {
switch (cursor) {
case MouseCursor::Inherit:
return nullptr;
case MouseCursor::Arrow:
return IDC_ARROW;
case MouseCursor::Wait:
return IDC_APPSTARTING;
case MouseCursor::Buzy:
return IDC_WAIT;
case MouseCursor::Beam:
return IDC_IBEAM;
case MouseCursor::SizeVertical:
return IDC_SIZENS;
case MouseCursor::SizeHorizonal:
return IDC_SIZEWE;
case MouseCursor::SizeDiagonalRight:
return IDC_SIZENESW;
case MouseCursor::SizeDiagonalLeft:
return IDC_SIZENWSE;
case MouseCursor::SizeAll:
return IDC_SIZEALL;
case MouseCursor::SplitHorizontal:
return IDC_ARROW;
case MouseCursor::SplitVertical:
return IDC_ARROW;
case MouseCursor::NoCursor:
return IDC_NO;
case MouseCursor::Pointer:
return IDC_HAND;
case MouseCursor::Forbidden:
return IDC_NO;
case MouseCursor::WhatsThis:
return IDC_HELP;
case MouseCursor::Cross:
return IDC_CROSS;
default:
break;
}
}

struct PlatformWindowWin32 : public PlatformWindow {
HWND hwnd;
};
Expand Down Expand Up @@ -228,8 +268,8 @@ static LRESULT CALLBACK svision_wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
}
break;

// case WM_MOVE:
// case WM_MOVING:
// case WM_MOVE:
// case WM_MOVING:
case WM_SIZE:
// case WM_SIZING:
{
Expand Down Expand Up @@ -303,37 +343,14 @@ auto PlatformWin32::init() -> void {

auto PlatformWin32::done() -> void {}

auto PlatformWin32::invalidate(PlatformWindow &w) -> void {
auto window = static_cast<PlatformWindowWin32 *>(&w);
InvalidateRect(window->hwnd, 0, 1);
}

auto PlatformWin32::main_loop() -> void {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) && !this->exit_loop) {
if (msg.message == WM_QUIT) {
spdlog::debug("Closing event loop, due to request from WM");
return;
}
TranslateMessage(&msg);
DispatchMessage(&msg);

if (this->close_on_last_window && total_open_windows == 0) {
spdlog::info("No more windows - closing event loop");
PostQuitMessage(0);
this->exit_loop = true;
}
}
}

auto PlatformWin32::open_window(int x, int y, int width, int height, const std::string &title)
-> std::shared_ptr<PlatformWindow> {
auto hInstance = GetModuleHandle(nullptr);
auto windowRect = RECT{0, 0, width, height};
AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
auto window_rect = RECT{0, 0, width, height};
AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE);

auto windowWidth = windowRect.right - windowRect.left;
auto windowHeight = windowRect.bottom - windowRect.top;
auto windowWidth = window_rect.right - window_rect.left;
auto windowHeight = window_rect.bottom - window_rect.top;

auto window = std::make_shared<PlatformWindowWin32>();
window->title = title;
Expand All @@ -355,3 +372,47 @@ auto PlatformWin32::show_window(std::shared_ptr<PlatformWindow> w) -> void {
ShowWindow(window->hwnd, SW_NORMAL);
UpdateWindow(window->hwnd);
}

auto PlatformWin32::set_cursor(PlatformWindow &window, MouseCursor cursor) -> void {
auto x11_window = static_cast<PlatformWindowWin32 *>(&window);
HCURSOR win32_cursor;

if (cursor_cache.find(cursor) == cursor_cache.end()) {
auto c = convert_mouse_cursor_to_win32(cursor);
win32_cursor = LoadCursor(nullptr, c);
cursor_cache[cursor] = win32_cursor;
spdlog::info("Caching new win32 cursor - {}, ({} so far)", (int)cursor,
cursor_cache.size());
} else {
win32_cursor = (HCURSOR)cursor_cache[cursor];
}
SetCursor(win32_cursor);
}

auto PlatformWin32::clear_cursor_cache() -> void {
// win32 cursors do not need to be destroyed using `DestroyCursor()`
cursor_cache.clear();
}

auto PlatformWin32::invalidate(PlatformWindow &w) -> void {
auto window = static_cast<PlatformWindowWin32 *>(&w);
InvalidateRect(window->hwnd, 0, 1);
}

auto PlatformWin32::main_loop() -> void {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) && !this->exit_loop) {
if (msg.message == WM_QUIT) {
spdlog::debug("Closing event loop, due to request from WM");
return;
}
TranslateMessage(&msg);
DispatchMessage(&msg);

if (this->close_on_last_window && total_open_windows == 0) {
spdlog::info("No more windows - closing event loop");
PostQuitMessage(0);
this->exit_loop = true;
}
}
}
7 changes: 6 additions & 1 deletion src/platformwin32.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@

#pragma once

#include <map>
#include <platform.h>

struct PlatformWin32 : Platform {
const std::string_view default_font_file = "c:\\Windows\\Fonts\\arial.ttf";
std::map<MouseCursor, void *> cursor_cache;

PlatformWin32();
virtual auto init() -> void override;
virtual auto done() -> void override;
virtual auto open_window(int x, int y, int width, int height, const std::string &title)
-> std::shared_ptr<PlatformWindow> override;
virtual auto show_window(std::shared_ptr<PlatformWindow> window) -> void override;
virtual auto invalidate(PlatformWindow &window) -> void;

virtual auto set_cursor(PlatformWindow &window, MouseCursor cursor) -> void override;
virtual auto clear_cursor_cache() -> void override;
virtual auto invalidate(PlatformWindow &window) -> void override;
virtual auto main_loop() -> void override;
};

Expand Down
Loading

0 comments on commit 09c3946

Please sign in to comment.