diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index f6854bbe..d19eeb9d 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -282,7 +282,7 @@ class Window { return res; } - protected bool dispatchMouseEvent(Widget root, MouseEvent event) { + protected bool dispatchMouseEvent(Widget root, MouseEvent event, ref bool cursorIsSet) { // only route mouse events to visible widgets if (root.visibility != Visibility.Visible) return false; @@ -291,9 +291,16 @@ class Window { // offer event to children first for (int i = 0; i < root.childCount; i++) { Widget child = root.child(i); - if (dispatchMouseEvent(child, event)) + if (dispatchMouseEvent(child, event, cursorIsSet)) return true; } + if (event.action == MouseAction.Move && !cursorIsSet) { + uint cursorType = root.getCursorType(event.x, event.y); + if (cursorType != CursorType.Parent) { + setCursorType(cursorType); + cursorIsSet = true; + } + } // if not processed by children, offer event to root if (sendAndCheckOverride(root, event)) { debug(mouse) Log.d("MouseEvent is processed"); @@ -445,6 +452,7 @@ class Window { if (event.action == MouseAction.Move || event.action == MouseAction.Leave) { processed = checkRemoveTracking(event); } + bool cursorIsSet = false; if (!res) { bool insideOneOfPopups = false; for (int i = cast(int)_popups.length - 1; i >= 0; i--) { @@ -458,11 +466,11 @@ class Window { if (p.onMouseEventOutside(event)) // stop loop when true is returned, but allow other main widget to handle event break; } else { - if (dispatchMouseEvent(p, event)) + if (dispatchMouseEvent(p, event, cursorIsSet)) return true; } } - res = dispatchMouseEvent(_mainWidget, event); + res = dispatchMouseEvent(_mainWidget, event, cursorIsSet); } return res || processed || _mainWidget.needDraw; } @@ -485,6 +493,10 @@ class Window { for (int i = 0; i < root.childCount; i++) checkUpdateNeeded(root.child(i), needDraw, needLayout, animationActive); } + /// sets cursor type for window + protected void setCursorType(uint cursorType) { + // override to support different mouse cursors + } /// checks content widgets for necessary redraw and/or layout bool checkUpdateNeeded(ref bool needDraw, ref bool needLayout, ref bool animationActive) { needDraw = needLayout = animationActive = false; diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index 5fbe7134..f3560caa 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -144,6 +144,69 @@ version(USE_SDL) { invalidate(); } + protected uint _lastCursorType = CursorType.None; + protected SDL_Cursor * [uint] _cursorMap; + /// sets cursor type for window + override protected void setCursorType(uint cursorType) { + // override to support different mouse cursors + if (_lastCursorType != cursorType) { + _lastCursorType = cursorType; + if (cursorType in _cursorMap) { + Log.d("changing cursor to ", cursorType); + SDL_SetCursor(_cursorMap[cursorType]); + return; + } + SDL_Cursor * cursor; + switch (cursorType) { + case CursorType.Arrow: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + break; + case CursorType.IBeam: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + break; + case CursorType.Wait: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); + break; + case CursorType.WaitArrow: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); + break; + case CursorType.Crosshair: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); + break; + case CursorType.No: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); + break; + case CursorType.Hand: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + break; + case CursorType.SizeNWSE: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + break; + case CursorType.SizeNESW: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); + break; + case CursorType.SizeWE: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + break; + case CursorType.SizeNS: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + break; + case CursorType.SizeAll: + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + break; + default: + // TODO: support custom cursors + cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + break; + } + if (cursor) { + Log.d("changing cursor to ", cursorType); + _cursorMap[cursorType] = cursor; + SDL_SetCursor(cursor); + } + } + } + SDL_Texture * _texture; int _txw; int _txh; @@ -191,12 +254,12 @@ version(USE_SDL) { float r = ((_backgroundColor >> 16) & 255) / 255.0f; float g = ((_backgroundColor >> 8) & 255) / 255.0f; float b = ((_backgroundColor >> 0) & 255) / 255.0f; - glClearColor(r, g, b, a); - glClear(GL_COLOR_BUFFER_BIT); - GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false); - buf.beforeDrawing(); + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT); + GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false); + buf.beforeDrawing(); onDraw(buf); - buf.afterDrawing(); + buf.afterDrawing(); SDL_GL_SwapWindow(_win); destroy(buf); } @@ -872,7 +935,7 @@ version(USE_SDL) { Log.e("Cannot load opengl library", e); } } - + SDL_DisplayMode displayMode; if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS) != 0) { Log.e("Cannot init SDL2"); @@ -880,13 +943,13 @@ version(USE_SDL) { } scope(exit)SDL_Quit(); int request = SDL_GetDesktopDisplayMode(0,&displayMode); - + version(USE_OPENGL) { - // we want OpenGL 3.3 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3); + // we want OpenGL 3.3 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2); - // Set OpenGL attributes - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + // Set OpenGL attributes + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); } diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index ab1907b6..5de662a7 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -936,6 +936,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener, MenuItemActionHandl return super.isActionEnabled(action); } } + /// shows popup at (x,y) override void showPopupMenu(int x, int y) { /// if preparation signal handler assigned, call it; don't show popup if false is returned from handler @@ -960,6 +961,12 @@ class EditWidgetBase : WidgetGroup, EditableContentListener, MenuItemActionHandl // TODO } + /// returns mouse cursor type for widget + override uint getCursorType(int x, int y) { + return CursorType.IBeam; + } + + /// when true, Tab / Shift+Tab presses are processed internally in widget (e.g. insert tab character) instead of focus change navigation. @property bool wantTabs() { return _wantTabs; diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index d5f30080..ddc50616 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -85,6 +85,25 @@ enum FocusMovement { Right, } +/// standard mouse cursor types +enum CursorType { + None, + /// use parent's cursor + Parent, + Arrow, + IBeam, + Wait, + Crosshair, + WaitArrow, + SizeNWSE, + SizeNESW, + SizeWE, + SizeNS, + SizeAll, + No, + Hand +} + class Widget { /// widget id protected string _id; @@ -121,6 +140,11 @@ class Widget { /// set new trackHover flag value (when true, widget will change Hover state while mouse is moving) @property Widget trackHover(bool v) { _trackHover = v; return this; } + /// returns mouse cursor type for widget + uint getCursorType(int x, int y) { + return CursorType.Arrow; + } + debug(resalloc) { private static int _instanceCount = 0; private static bool _appShuttingDown = false;