UI: Ghost: support horizontal scrolling for 2D editors
Some mice have an additional horizontal scroll wheel. This patch adds support for receiving such events. By default it is used to scroll 2D editors left and right. I originally developed this because I was missing it in the spreadsheet, but it seems to be useful in many other editors too. It's supported on Linux (Wayland), Windows and macos. Pull Request: https://projects.blender.org/blender/blender/pulls/138758
This commit is contained in:
@@ -260,7 +260,7 @@ typedef enum {
|
||||
/** Mouse button up event. */
|
||||
GHOST_kEventButtonUp,
|
||||
/**
|
||||
* Mouse wheel event.
|
||||
* Vertical/Horizontal mouse wheel event.
|
||||
*
|
||||
* \note #GHOST_GetEventData returns #GHOST_TEventWheelData.
|
||||
*/
|
||||
@@ -577,9 +577,16 @@ typedef struct {
|
||||
GHOST_TabletData tablet;
|
||||
} GHOST_TEventButtonData;
|
||||
|
||||
typedef enum {
|
||||
GHOST_kEventWheelAxisVertical = 0,
|
||||
GHOST_kEventWheelAxisHorizontal = 1,
|
||||
} GHOST_TEventWheelAxis;
|
||||
|
||||
typedef struct {
|
||||
/** Which mouse wheel is used. */
|
||||
GHOST_TEventWheelAxis axis;
|
||||
/** Displacement of a mouse wheel. */
|
||||
int32_t z;
|
||||
int32_t value;
|
||||
} GHOST_TEventWheelData;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -80,7 +80,9 @@ bool GHOST_EventPrinter::processEvent(const GHOST_IEvent *event)
|
||||
}
|
||||
case GHOST_kEventWheel: {
|
||||
const GHOST_TEventWheelData *wheelData = static_cast<const GHOST_TEventWheelData *>(data);
|
||||
std::cout << "GHOST_kEventWheel, z: " << wheelData->z;
|
||||
std::cout << "GHOST_kEventWheel, axis: "
|
||||
<< (wheelData->axis == GHOST_kEventWheelAxisVertical ? "vertical" : "horizontal")
|
||||
<< ", value: " << wheelData->value;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -22,16 +22,17 @@ class GHOST_EventWheel : public GHOST_Event {
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param window: The window of this event.
|
||||
* \param z: The displacement of the mouse wheel.
|
||||
* \param axis: The axis of the mouse wheel.
|
||||
* \param value: The displacement of the mouse wheel.
|
||||
*/
|
||||
GHOST_EventWheel(uint64_t msec, GHOST_IWindow *window, int32_t z)
|
||||
GHOST_EventWheel(uint64_t msec, GHOST_IWindow *window, GHOST_TEventWheelAxis axis, int32_t value)
|
||||
: GHOST_Event(msec, GHOST_kEventWheel, window)
|
||||
{
|
||||
m_wheelEventData.z = z;
|
||||
m_wheelEventData.axis = axis;
|
||||
m_wheelEventData.value = value;
|
||||
m_data = &m_wheelEventData;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The z-displacement of the mouse wheel. */
|
||||
GHOST_TEventWheelData m_wheelEventData;
|
||||
};
|
||||
|
||||
@@ -1743,17 +1743,16 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
|
||||
/* Standard scroll-wheel case, if no swiping happened,
|
||||
* and no momentum (kinetic scroll) works. */
|
||||
if (!m_multiTouchScroll && momentumPhase == NSEventPhaseNone) {
|
||||
double deltaF = event.deltaY;
|
||||
|
||||
if (deltaF == 0.0) {
|
||||
deltaF = event.deltaX; /* Make blender decide if it's horizontal scroll. */
|
||||
if (event.deltaX != 0.0) {
|
||||
const int32_t delta = event.deltaX > 0.0 ? 1 : -1;
|
||||
pushEvent(new GHOST_EventWheel(
|
||||
event.timestamp * 1000, window, GHOST_kEventWheelAxisHorizontal, delta));
|
||||
}
|
||||
if (deltaF == 0.0) {
|
||||
break; /* Discard trackpad delta=0 events. */
|
||||
if (event.deltaY != 0.0) {
|
||||
const int32_t delta = event.deltaY > 0.0 ? 1 : -1;
|
||||
pushEvent(new GHOST_EventWheel(
|
||||
event.timestamp * 1000, window, GHOST_kEventWheelAxisVertical, delta));
|
||||
}
|
||||
|
||||
const int32_t delta = deltaF > 0.0 ? 1 : -1;
|
||||
pushEvent(new GHOST_EventWheel(event.timestamp * 1000, window, delta));
|
||||
}
|
||||
else {
|
||||
const NSPoint mousePos = event.locationInWindow;
|
||||
|
||||
@@ -605,7 +605,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
|
||||
GHOST_WindowSDL *window = findGhostWindow(
|
||||
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
||||
assert(window != nullptr);
|
||||
g_event = new GHOST_EventWheel(event_ms, window, sdl_sub_evt.y);
|
||||
g_event = new GHOST_EventWheel(
|
||||
event_ms, window, GHOST_kEventWheelAxisVertical, sdl_sub_evt.y);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYDOWN:
|
||||
|
||||
@@ -4110,13 +4110,19 @@ static void pointer_handle_frame(void *data, wl_pointer * /*wl_pointer*/)
|
||||
}
|
||||
|
||||
/* Done evaluating scroll input, generate the events. */
|
||||
|
||||
/* Discrete X axis currently unsupported. */
|
||||
if (ps.discrete_xy[0] || ps.discrete_xy[1]) {
|
||||
if (ps.discrete_xy[0]) {
|
||||
seat->system->pushEvent_maybe_pending(new GHOST_EventWheel(
|
||||
ps.has_event_ms ? ps.event_ms : seat->system->getMilliSeconds(),
|
||||
win,
|
||||
GHOST_kEventWheelAxisHorizontal,
|
||||
ps.discrete_xy[0]));
|
||||
}
|
||||
if (ps.discrete_xy[1]) {
|
||||
seat->system->pushEvent_maybe_pending(new GHOST_EventWheel(
|
||||
ps.has_event_ms ? ps.event_ms : seat->system->getMilliSeconds(),
|
||||
win,
|
||||
GHOST_kEventWheelAxisVertical,
|
||||
-ps.discrete_xy[1]));
|
||||
}
|
||||
ps.discrete_xy[0] = 0;
|
||||
@@ -4949,7 +4955,10 @@ static void tablet_tool_handle_frame(void *data,
|
||||
}
|
||||
case GWL_TabletTool_EventTypes::Wheel: {
|
||||
seat->system->pushEvent_maybe_pending(
|
||||
new GHOST_EventWheel(event_ms, win, -tablet_tool->frame_pending.wheel.clicks));
|
||||
new GHOST_EventWheel(event_ms,
|
||||
win,
|
||||
GHOST_kEventWheelAxisVertical,
|
||||
-tablet_tool->frame_pending.wheel.clicks));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1217,13 +1217,13 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
||||
GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
|
||||
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window,
|
||||
WPARAM wParam,
|
||||
LPARAM /*lParam*/)
|
||||
void GHOST_SystemWin32::processWheelEventVertical(GHOST_WindowWin32 *window,
|
||||
WPARAM wParam,
|
||||
LPARAM /*lParam*/)
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
int acc = system->m_wheelDeltaAccum;
|
||||
int acc = system->m_wheelDeltaAccumVertical;
|
||||
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
|
||||
if (acc * delta < 0) {
|
||||
@@ -1235,10 +1235,37 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window,
|
||||
acc = abs(acc);
|
||||
|
||||
while (acc >= WHEEL_DELTA) {
|
||||
system->pushEvent(new GHOST_EventWheel(getMessageTime(system), window, direction));
|
||||
system->pushEvent(new GHOST_EventWheel(
|
||||
getMessageTime(system), window, GHOST_kEventWheelAxisVertical, direction));
|
||||
acc -= WHEEL_DELTA;
|
||||
}
|
||||
system->m_wheelDeltaAccum = acc * direction;
|
||||
system->m_wheelDeltaAccumVertical = acc * direction;
|
||||
}
|
||||
|
||||
/** This is almost the same as #processWheelEventVertical. */
|
||||
void GHOST_SystemWin32::processWheelEventHorizontal(GHOST_WindowWin32 *window,
|
||||
WPARAM wParam,
|
||||
LPARAM /*lParam*/)
|
||||
{
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
int acc = system->m_wheelDeltaAccumHorizontal;
|
||||
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
|
||||
if (acc * delta < 0) {
|
||||
/* Scroll direction reversed. */
|
||||
acc = 0;
|
||||
}
|
||||
acc += delta;
|
||||
int direction = (acc >= 0) ? 1 : -1;
|
||||
acc = abs(acc);
|
||||
|
||||
while (acc >= WHEEL_DELTA) {
|
||||
system->pushEvent(new GHOST_EventWheel(
|
||||
getMessageTime(system), window, GHOST_kEventWheelAxisHorizontal, direction));
|
||||
acc -= WHEEL_DELTA;
|
||||
}
|
||||
system->m_wheelDeltaAccumHorizontal = acc * direction;
|
||||
}
|
||||
|
||||
GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw)
|
||||
@@ -1994,13 +2021,18 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
|
||||
* since DefWindowProc propagates it up the parent chain
|
||||
* until it finds a window that processes it.
|
||||
*/
|
||||
processWheelEvent(window, wParam, lParam);
|
||||
processWheelEventVertical(window, wParam, lParam);
|
||||
eventHandled = true;
|
||||
#ifdef BROKEN_PEEK_TOUCHPAD
|
||||
PostMessage(hwnd, WM_USER, 0, 0);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEHWHEEL: {
|
||||
processWheelEventHorizontal(window, wParam, lParam);
|
||||
eventHandled = true;
|
||||
break;
|
||||
}
|
||||
case WM_SETCURSOR: {
|
||||
/* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
|
||||
* to move within a window and mouse input is not captured.
|
||||
@@ -2072,7 +2104,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
|
||||
* If the windows use different input queues, the message is sent asynchronously,
|
||||
* so the window is activated immediately. */
|
||||
|
||||
system->m_wheelDeltaAccum = 0;
|
||||
system->m_wheelDeltaAccumVertical = 0;
|
||||
system->m_wheelDeltaAccumHorizontal = 0;
|
||||
event = processWindowEvent(
|
||||
LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
|
||||
/* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
|
||||
|
||||
@@ -369,12 +369,20 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
const int32_t screen_co[2]);
|
||||
|
||||
/**
|
||||
* Handles a mouse wheel event.
|
||||
* Handles a vertical mouse wheel event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param wParam: The wParam from the `wndproc`.
|
||||
* \param lParam: The lParam from the `wndproc`.
|
||||
*/
|
||||
static void processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam);
|
||||
static void processWheelEventVertical(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Handles a horizontal mouse wheel event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param wParam: The wParam from the `wndproc`.
|
||||
* \param lParam: The lParam from the `wndproc`.
|
||||
*/
|
||||
static void processWheelEventHorizontal(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Creates a key event and updates the key data stored locally (m_modifierKeys).
|
||||
@@ -479,8 +487,9 @@ class GHOST_SystemWin32 : public GHOST_System {
|
||||
/** Console status. */
|
||||
bool m_consoleStatus;
|
||||
|
||||
/** Wheel delta accumulator. */
|
||||
int m_wheelDeltaAccum;
|
||||
/** Wheel delta accumulators. */
|
||||
int m_wheelDeltaAccumVertical;
|
||||
int m_wheelDeltaAccumHorizontal;
|
||||
};
|
||||
|
||||
inline void GHOST_SystemWin32::handleKeyboardChange()
|
||||
|
||||
@@ -1268,13 +1268,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
||||
/* process wheel mouse events and break, only pass on press events */
|
||||
if (xbe.button == Button4) {
|
||||
if (xbe.type == ButtonPress) {
|
||||
g_event = new GHOST_EventWheel(event_ms, window, 1);
|
||||
g_event = new GHOST_EventWheel(event_ms, window, GHOST_kEventWheelAxisVertical, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (xbe.button == Button5) {
|
||||
if (xbe.type == ButtonPress) {
|
||||
g_event = new GHOST_EventWheel(event_ms, window, -1);
|
||||
g_event = new GHOST_EventWheel(event_ms, window, GHOST_kEventWheelAxisVertical, -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ bool processEvent(GHOST_EventHandle hEvent, GHOST_TUserDataPtr userData)
|
||||
#endif
|
||||
case GHOST_kEventWheel: {
|
||||
wheelData = (GHOST_TEventWheelData *)GHOST_GetEventData(hEvent);
|
||||
if (wheelData->z > 0) {
|
||||
if (wheelData->value > 0) {
|
||||
view_rotz += 5.f;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -459,7 +459,7 @@ bool Application::processEvent(const GHOST_IEvent *event)
|
||||
#endif
|
||||
case GHOST_kEventWheel: {
|
||||
GHOST_TEventWheelData *wheelData = (GHOST_TEventWheelData *)event->getData();
|
||||
if (wheelData->z > 0) {
|
||||
if (wheelData->value > 0) {
|
||||
view_rotz += 5.f;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -939,7 +939,9 @@ def km_view2d(_params):
|
||||
("view2d.pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
|
||||
("view2d.pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
|
||||
("view2d.scroll_right", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True}, None),
|
||||
("view2d.scroll_right", {"type": 'WHEELRIGHTMOUSE', "value": 'PRESS'}, None),
|
||||
("view2d.scroll_left", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True}, None),
|
||||
("view2d.scroll_left", {"type": 'WHEELLEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
|
||||
("view2d.scroll_up", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
|
||||
("view2d.ndof", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
|
||||
|
||||
@@ -71,6 +71,8 @@ static const EnumPropertyItem event_mouse_type_items[] = {
|
||||
{WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, CTX_N_(BLT_I18NCONTEXT_UI_EVENTS, "Wheel Down"), ""},
|
||||
{WHEELINMOUSE, "WHEELINMOUSE", 0, CTX_N_(BLT_I18NCONTEXT_UI_EVENTS, "Wheel In"), ""},
|
||||
{WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, CTX_N_(BLT_I18NCONTEXT_UI_EVENTS, "Wheel Out"), ""},
|
||||
{WHEELLEFTMOUSE, "WHEELLEFTMOUSE", 0, CTX_N_(BLT_I18NCONTEXT_UI_EVENTS, "Wheel Left"), ""},
|
||||
{WHEELRIGHTMOUSE, "WHEELRIGHTMOUSE", 0, CTX_N_(BLT_I18NCONTEXT_UI_EVENTS, "Wheel Right"), ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
@@ -236,6 +238,8 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
|
||||
{WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, "Wheel Down", "WhDown"},
|
||||
{WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", "WhIn"},
|
||||
{WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, "Wheel Out", "WhOut"},
|
||||
{WHEELLEFTMOUSE, "WHEELLEFTMOUSE", 0, "Wheel Left", "WhLeft"},
|
||||
{WHEELRIGHTMOUSE, "WHEELRIGHTMOUSE", 0, "Wheel Right", "WhRight"},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{EVT_AKEY, "A", 0, "A", ""},
|
||||
{EVT_BKEY, "B", 0, "B", ""},
|
||||
|
||||
@@ -6257,13 +6257,25 @@ void wm_event_add_ghostevent(wmWindowManager *wm,
|
||||
customdata);
|
||||
|
||||
int click_step;
|
||||
if (wheelData->z > 0) {
|
||||
event.type = WHEELUPMOUSE;
|
||||
click_step = wheelData->z;
|
||||
if (wheelData->axis == GHOST_kEventWheelAxisVertical) {
|
||||
if (wheelData->value > 0) {
|
||||
event.type = WHEELUPMOUSE;
|
||||
click_step = wheelData->value;
|
||||
}
|
||||
else {
|
||||
event.type = WHEELDOWNMOUSE;
|
||||
click_step = -wheelData->value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
event.type = WHEELDOWNMOUSE;
|
||||
click_step = -wheelData->z;
|
||||
if (wheelData->value > 0) {
|
||||
event.type = WHEELRIGHTMOUSE;
|
||||
click_step = wheelData->value;
|
||||
}
|
||||
else {
|
||||
event.type = WHEELLEFTMOUSE;
|
||||
click_step = -wheelData->value;
|
||||
}
|
||||
}
|
||||
BLI_assert(click_step != 0);
|
||||
|
||||
|
||||
@@ -76,16 +76,20 @@ enum wmEventType : int16_t {
|
||||
* ignore all but the most recent MOUSEMOVE (for better performance),
|
||||
* paint and drawing tools however will want to handle these. */
|
||||
INBETWEEN_MOUSEMOVE = 0x0011,
|
||||
/* Horizontal scrolling events. */
|
||||
WHEELLEFTMOUSE = 0x0014, /* 20 */
|
||||
WHEELRIGHTMOUSE = 0x0015, /* 21 */
|
||||
|
||||
/* Maximum keyboard value (inclusive). */
|
||||
#define _EVT_MOUSE_MAX 0x0011 /* 17 */
|
||||
#define _EVT_MOUSE_MAX 0x0015 /* 21 */
|
||||
|
||||
/* IME event, GHOST_kEventImeCompositionStart in ghost. */
|
||||
WM_IME_COMPOSITE_START = 0x0014,
|
||||
WM_IME_COMPOSITE_START = 0x0016,
|
||||
/* 0x0017 is MOUSESMARTZOOM. */
|
||||
/* IME event, GHOST_kEventImeComposition in ghost. */
|
||||
WM_IME_COMPOSITE_EVENT = 0x0015,
|
||||
WM_IME_COMPOSITE_EVENT = 0x0018,
|
||||
/* IME event, GHOST_kEventImeCompositionEnd in ghost. */
|
||||
WM_IME_COMPOSITE_END = 0x0016,
|
||||
WM_IME_COMPOSITE_END = 0x0019,
|
||||
|
||||
/* Tablet/Pen Specific Events. */
|
||||
TABLET_STYLUS = 0x001a,
|
||||
@@ -424,7 +428,9 @@ enum wmEventType : int16_t {
|
||||
BUTTON6MOUSE, \
|
||||
BUTTON7MOUSE))
|
||||
/** Test whether the event is a mouse wheel. */
|
||||
#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
|
||||
#define ISMOUSE_WHEEL(event_type) \
|
||||
(((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE) || \
|
||||
ELEM((event_type), WHEELLEFTMOUSE, WHEELRIGHTMOUSE))
|
||||
/** Test whether the event is a mouse (trackpad) gesture. */
|
||||
#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSESMARTZOOM)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user