diff --git a/examples/example1/src/example1.d b/examples/example1/src/example1.d index 5d56e5c4..9a66d204 100644 --- a/examples/example1/src/example1.d +++ b/examples/example1/src/example1.d @@ -266,7 +266,9 @@ extern (C) int UIAppMain(string[] args) { //} // create window - Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 800, 700); + //Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 800, 700); + // Expand window size if content is bigger than 800, 700 (change to above version if you want scrollbars and 800, 700 size) + Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable | WindowFlag.ExpandSize, 800, 700); // here you can see window or content resize mode //Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 400, 400); //window.windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 0b085c47..780589d7 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -56,6 +56,8 @@ enum WindowFlag : uint { MeasureSize = 8, /// window without decorations Borderless = 16, + /// expand window size if main widget minimal size is greater than size defined in window constructor + ExpandSize = 32, } /// Window states @@ -333,9 +335,9 @@ class Window : CustomEventTarget { @property bool caretReplace() { return _caretReplace; } // window content resize mode - protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; + //protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; //protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.shrinkWidgets; - //protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.scrollWindow; + protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.scrollWindow; @property WindowOrContentResizeMode windowOrContentResizeMode() {return _windowOrContentResizeMode; } @property void windowOrContentResizeMode(WindowOrContentResizeMode newMode) { @@ -466,7 +468,7 @@ class Window : CustomEventTarget { void adjustWindowOrContentSize(int minContentWidth, int minContentHeight) { _minContentWidth = minContentWidth; _minContentHeight = minContentHeight; - if (_windowOrContentResizeMode == WindowOrContentResizeMode.resizeWindow) + if (_windowOrContentResizeMode == WindowOrContentResizeMode.resizeWindow || flags & WindowFlag.ExpandSize) resizeWindow(Point(max(_windowRect.right, minContentWidth), max(_windowRect.bottom, minContentHeight))); updateWindowOrContentSize(); } diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index 265aecd6..941bcc0a 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -412,7 +412,19 @@ class SDLWindow : Window { if (_windowState != WindowState.normal) { SDL_RestoreWindow(_win); version(linux) { - // some SDL versions, reset windows size and position to values from create window (SDL 2.0.4) on linux (don't know how it works on macOS) + // On linux with Cinnamon desktop, changing window state from for example minimized reset windows size + // and/or position to values from create window (last tested on Cinamon 3.4.6 with SDL 2.0.4) + // + // Steps to reproduce: + // Need app with two windows - dlangide for example. + // 1. Comment this fix + // 2. dub run --force + // 3. After first window appear move it and/or change window size + // 4. Click on button to open file + // 5. Click on window icon minimize in open file dialog + // 6. Restore window clicking on taskbar + // 7. The first main window has old position/size + // Xfce works OK without this fix if (newWindowRect.bottom == int.min && newWindowRect.right == int.min) SDL_SetWindowSize(_win, _windowRect.right, _windowRect.bottom); diff --git a/src/dlangui/platforms/x11/x11app.d b/src/dlangui/platforms/x11/x11app.d index fc8bc146..320af040 100644 --- a/src/dlangui/platforms/x11/x11app.d +++ b/src/dlangui/platforms/x11/x11app.d @@ -101,6 +101,7 @@ private __gshared Atom atom_DLANGUI_TASK_EVENT; Atom atom_DLANGUI_CLOSE_WINDOW_EVENT; Atom atom_DLANGUI_CLIPBOARD_BUFFER; + Atom atom_DLANGUI_REDRAW_EVENT; } static void setupX11Atoms() @@ -127,6 +128,7 @@ static void setupX11Atoms() atom_DLANGUI_TASK_EVENT = XInternAtom(x11display, "DLANGUI_TASK_EVENT", False); atom_DLANGUI_CLOSE_WINDOW_EVENT = XInternAtom(x11display, "DLANGUI_CLOSE_WINDOW_EVENT", False); atom_DLANGUI_CLIPBOARD_BUFFER = XInternAtom(x11display, "DLANGUI_CLIPBOARD_BUFFER", False); + atom_DLANGUI_REDRAW_EVENT = XInternAtom(x11display, "DLANGUI_REDRAW_EVENT", False); } // Cursor font constants @@ -254,7 +256,6 @@ class X11Window : DWindow { X11Window[] _children; X11Window _parent; - bool _needRedraw; int _cachedWidth, _cachedHeight; static if (ENABLE_OPENGL) { @@ -440,11 +441,19 @@ class X11Window : DWindow { } } + private bool hasVisibleModalChild() { + foreach (X11Window w;_children) { + if (w.flags & WindowFlag.Modal && w._windowState != WindowState.hidden) + return true; + } + return false; + } + /// show window override void show() { Log.d("X11Window.show"); XMapRaised(x11display, _win); - XFlush(x11display); + static if (ENABLE_OPENGL) { if (_enableOpengl) { _glc = glXCreateContext(x11display, x11visual, null, GL_TRUE); @@ -476,6 +485,7 @@ class X11Window : DWindow { _mainWidget.setFocus(); } + XFlush(x11display); } override protected void handleWindowStateChange(WindowState newState, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) { @@ -652,12 +662,18 @@ class X11Window : DWindow { XChangeProperty(x11display, _win, atom_NET_WM_ICON, XA_CARDINAL, 32, PropModeReplace, cast(ubyte*)propData.ptr, cast(int)propData.length); } + uint _lastRedrawEventCode; /// request window redraw override void invalidate() { - if (!_needRedraw) { - debug(x11) Log.d("Window.invalidate()"); - _needRedraw = true; - } + XEvent ev; + ev.xclient.type = ClientMessage; + ev.xclient.message_type = atom_DLANGUI_REDRAW_EVENT; + ev.xclient.window = _win; + ev.xclient.display = x11display; + ev.xclient.format = 32; + ev.xclient.data.l[0] = ++_lastRedrawEventCode; + XSendEvent(x11display, _win, false, StructureNotifyMask, &ev); + XFlush(x11display); } /// close window @@ -729,7 +745,7 @@ class X11Window : DWindow { } void redraw() { - _needRedraw = false; + _lastRedrawEventCode = 0; //Use values cached by ConfigureNotify to avoid XGetWindowAttributes call. //XWindowAttributes window_attributes_return; //XGetWindowAttributes(x11display, _win, &window_attributes_return); @@ -749,14 +765,39 @@ class X11Window : DWindow { protected ButtonDetails _mbutton; protected ButtonDetails _rbutton; - ushort convertMouseFlags(uint flags) { + // x11 gives flags from time prior event so if left button is pressed there is not Button1Mask + ushort convertMouseFlags(uint flags, MouseButton btn, bool pressed) { ushort res = 0; - if (flags & Button1Mask) - res |= MouseFlag.LButton; - if (flags & Button2Mask) - res |= MouseFlag.RButton; - if (flags & Button3Mask) - res |= MouseFlag.MButton; + if (btn == MouseButton.Left) { + if (pressed) + res |= MouseFlag.LButton; + else + res &= ~MouseFlag.LButton; + } + else + if (flags & Button1Mask) + res |= MouseFlag.LButton; + + if (btn == MouseButton.Middle) { + if (pressed) + res |= MouseFlag.MButton; + else + res &= ~MouseFlag.MButton; + } + else + if (flags & Button2Mask) + res |= MouseFlag.MButton; + + if (btn == MouseButton.Right) { + if (pressed) + res |= MouseFlag.RButton; + else + res &= ~MouseFlag.RButton; + } + else + if (flags & Button3Mask) + res |= MouseFlag.RButton; + return res; } @@ -764,9 +805,9 @@ class X11Window : DWindow { if (button == Button1) return MouseButton.Left; if (button == Button2) - return MouseButton.Right; - if (button == Button3) return MouseButton.Middle; + if (button == Button3) + return MouseButton.Right; return MouseButton.None; } @@ -794,7 +835,9 @@ class X11Window : DWindow { if (wheelDelta) event = new MouseEvent(action, MouseButton.None, lastFlags, lastx, lasty, wheelDelta); } else { - lastFlags = convertMouseFlags(state); + MouseButton btn = convertMouseButton(button); + lastFlags = convertMouseFlags(state, btn, action == MouseAction.ButtonDown); + if (_keyFlags & KeyFlag.Shift) lastFlags |= MouseFlag.Shift; if (_keyFlags & KeyFlag.Control) @@ -803,7 +846,6 @@ class X11Window : DWindow { lastFlags |= MouseFlag.Alt; lastx = cast(short)x; lasty = cast(short)y; - MouseButton btn = convertMouseButton(button); event = new MouseEvent(action, btn, lastFlags, lastx, lasty); } if (event) { @@ -824,6 +866,7 @@ class X11Window : DWindow { event.lbutton = _lbutton; event.rbutton = _rbutton; event.mbutton = _mbutton; + bool res = dispatchMouseEvent(event); if (res) { debug(mouse) Log.d("Calling update() after mouse event"); @@ -1237,7 +1280,6 @@ class X11Platform : Platform { } private X11Window[XWindow] _windowMap; - private X11Window[] _windowList; /** * create window @@ -1255,7 +1297,6 @@ class X11Platform : Platform { int newheight = height; X11Window window = new X11Window(this, windowCaption, parent, flags, newwidth, newheight); _windowMap[window._win] = window; - _windowList ~= window; return window; } @@ -1284,17 +1325,6 @@ class X11Platform : Platform { XSendEvent(x11display2, window._win, false, StructureNotifyMask, &ev); XFlush(x11display2); XUnlockDisplay(x11display2); - - for (uint i = 0; i < _windowList.length; i++) { - if (w is _windowList[i]) { - for (uint j = i; j + 1 < _windowList.length; j++) - _windowList[j] = _windowList[j + 1]; - _windowList[$ - 1] = null; - _windowList.length--; - break; - } - } - } bool handleTimers() { @@ -1312,7 +1342,7 @@ class X11Platform : Platform { return _windowMap.length == 0; } - protected int numberOfPendingEvents() + protected int numberOfPendingEvents(int msecs = 10) { import core.sys.posix.sys.select; int x11displayFd = ConnectionNumber(x11display); @@ -1326,13 +1356,14 @@ class X11Platform : Platform { import core.stdc.errno; int selectResult; do { - timeval zeroTime; - selectResult = select(x11displayFd + 1, &fdSet, null, null, &zeroTime); + timeval timeout; + timeout.tv_usec = msecs; + selectResult = select(x11displayFd + 1, &fdSet, null, null, &timeout); } while(selectResult == -1 && errno == EINTR); if (selectResult < 0) { Log.e("X11: display fd select error"); } else if (selectResult == 1) { - Log.d("X11: XPending"); + //Log.d("X11: XPending"); eventsInQueue = XPending(x11display); } } @@ -1482,7 +1513,11 @@ class X11Platform : Platform { Log.d("X11: ButtonPress event"); X11Window w = findWindow(event.xbutton.window); if (w) { - w.processMouseEvent(MouseAction.ButtonDown, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y); + if (event.xbutton.button == 4 || event.xbutton.button == 5) { + w.processMouseEvent(MouseAction.Wheel, 0, 0, 0, event.xbutton.button == 4 ? 1 : -1); + } else { + w.processMouseEvent(MouseAction.ButtonDown, event.xbutton.button, event.xbutton.state, event.xbutton.x, event.xbutton.y); + } } else { Log.e("Window not found"); } @@ -1508,7 +1543,9 @@ class X11Platform : Platform { case EnterNotify: Log.d("X11: EnterNotify event"); X11Window w = findWindow(event.xcrossing.window); - if (!w) { + if (w) { + w.processMouseEvent(MouseAction.Move, 0, event.xmotion.state, event.xcrossing.x, event.xcrossing.y); + } else { Log.e("Window not found"); } break; @@ -1616,6 +1653,9 @@ class X11Platform : Platform { w.handlePostedEvent(cast(uint)event.xclient.data.l[0]); } else if (event.xclient.message_type == atom_DLANGUI_TIMER_EVENT) { w.handleTimer(); + } else if (event.xclient.message_type == atom_DLANGUI_REDRAW_EVENT) { + if (event.xclient.data.l[0] == w._lastRedrawEventCode) + w.redraw(); } else if (event.xclient.message_type == atom_WM_PROTOCOLS) { Log.d("Handling WM_PROTOCOLS"); if ((event.xclient.format == 32) && (event.xclient.data.l[0]) == atom_WM_DELETE_WINDOW) { @@ -1636,41 +1676,30 @@ class X11Platform : Platform { } } + protected void pumpEvents() + { + XFlush(x11display); + // Note: only events we set the mask for are detected! + while(numberOfPendingEvents()) + { + if (allWindowsClosed()) + break; + XEvent event; /* the XEvent declaration !!! */ + XNextEvent(x11display, &event); + processXEvent(event); + } + } + /** * Starts application message loop. * * When returned from this method, application is shutting down. */ override int enterMessageLoop() { - import core.thread; - XEvent event; /* the XEvent declaration !!! */ - KeySym key; /* a dealie-bob to handle KeyPress Events */ - char[255] text; /* a char buffer for KeyPress Events */ - Log.d("enterMessageLoop()"); while(!allWindowsClosed()) { - // Note: only events we set the mask for are detected! - foreach(win; _windowMap) { - if (win._needRedraw) { - win.redraw(); - } - } - XFlush(x11display); - int eventsInQueue = numberOfPendingEvents(); - if (!eventsInQueue) { - debug(x11) Log.d("X11: Sleeping"); - Thread.sleep(dur!("msecs")(10)); - } - foreach(eventIndex; 0..eventsInQueue) - { - if (allWindowsClosed()) - break; - if (!numberOfPendingEvents()) - break; - XNextEvent(x11display, &event); - processXEvent(event); - } + pumpEvents(); } return 0; } @@ -1710,18 +1739,7 @@ class X11Platform : Platform { Log.e("Waiting for clipboard contents timeout"); return ""d; } - XFlush(x11display); - int eventsInQueue = numberOfPendingEvents(); - foreach(eventIndex; 0..eventsInQueue) - { - if (allWindowsClosed()) - break; - if (!numberOfPendingEvents()) - break; - XEvent event; - XNextEvent(x11display, &event); - processXEvent(event); - } + pumpEvents(); } Atom selectionTarget; int selectionFormat; @@ -1780,15 +1798,9 @@ class X11Platform : Platform { /// returns true if there is some modal window opened above this window, and this window should not process mouse/key input and should not allow closing override bool hasModalWindowsAbove(DWindow w) { - // override in platform specific class - for (uint i = 0; i + 1 < _windowList.length; i++) { - if (_windowList[i] is w) { - for (uint j = i + 1; j < _windowList.length; j++) { - if (_windowList[j].flags & WindowFlag.Modal && _windowList[j].windowState != WindowState.hidden) - return true; - } - return false; - } + X11Window x11Win = cast (X11Window) w; + if (x11Win) { + return x11Win.hasVisibleModalChild(); } return false; }