From fa5ddfe602df9f3e2c809e2d24c9dda876bb9d8e Mon Sep 17 00:00:00 2001 From: ZILtoid1991 Date: Sun, 26 May 2024 22:08:27 +0200 Subject: [PATCH] Implementing resizable windows --- .../concrete/types/stylesheet.d | 2 + .../src/pixelperfectengine/concrete/window.d | 182 ++++++++++++++++-- .../concrete/windowhandler.d | 3 + test3/app.d | 15 ++ 4 files changed, 189 insertions(+), 13 deletions(-) diff --git a/pixelperfectengine/src/pixelperfectengine/concrete/types/stylesheet.d b/pixelperfectengine/src/pixelperfectengine/concrete/types/stylesheet.d index 80b1d21..e29e033 100644 --- a/pixelperfectengine/src/pixelperfectengine/concrete/types/stylesheet.d +++ b/pixelperfectengine/src/pixelperfectengine/concrete/types/stylesheet.d @@ -129,6 +129,8 @@ public class StyleSheet{ drawParameters["defDialogPadding"] = 10; + drawParameters["windowMinimumSizes"] = 64; + pattern["blackDottedLine"] = [0x1f, 0x1f, 0x10, 0x10]; } /** diff --git a/pixelperfectengine/src/pixelperfectengine/concrete/window.d b/pixelperfectengine/src/pixelperfectengine/concrete/window.d index 17569e8..1ae55a8 100644 --- a/pixelperfectengine/src/pixelperfectengine/concrete/window.d +++ b/pixelperfectengine/src/pixelperfectengine/concrete/window.d @@ -46,16 +46,17 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { //public int header;//, sizeX, sizeY; protected int moveX, moveY; ///Relative x and y coordinates for drag events protected uint flags; ///Stores various flags - protected static enum IS_ACTIVE = 1 << 0; - protected static enum NEEDS_FULL_UPDATE = 1 << 1; - protected static enum HEADER_UPDATE = 1 << 2; - protected static enum IS_MOVED = 1 << 3; - protected static enum IS_RESIZED = 1 << 4; - protected static enum IS_RESIZED_L = 1 << 5; - protected static enum IS_RESIZED_T = 1 << 6; - protected static enum IS_RESIZED_B = 1 << 7; - protected static enum IS_RESIZED_R = 1 << 8; - protected static enum IS_RESIZABLE_BY_MOUSE = 1 << 9; + protected static enum IS_ACTIVE = 1 << 0; ///Set if window is active + protected static enum NEEDS_FULL_UPDATE = 1 << 1;///Set if window needs full redraw (Deprecated) + protected static enum HEADER_UPDATE = 1 << 2; ///Set if header needs redraw (Deprecated) + protected static enum IS_MOVED = 1 << 3; ///Set if window is being moved + protected static enum IS_RESIZED = 0xF0; ///Used to test if window is being resized or not + protected static enum IS_RESIZED_L = 1 << 4; ///Set if window is being resized by the left edge + protected static enum IS_RESIZED_T = 1 << 5; ///Set if window is being resized by the top edge + protected static enum IS_RESIZED_B = 1 << 6; ///Set if window is being resized by the bottom edge + protected static enum IS_RESIZED_R = 1 << 7; ///Set if window is being resized by the right edge + protected static enum IS_RESIZABLE_BY_MOUSE_H = 1 << 8;///Set if window is horizontally resizable + protected static enum IS_RESIZABLE_BY_MOUSE_V = 1 << 9;///Set if window is vertically resizable //protected bool fullUpdate; ///True if window needs full redraw //protected bool isActive; ///True if window is currently active //protected bool headerUpdate; ///True if needs header update @@ -242,7 +243,7 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { drawTextSL(headerArea, title, Point(0,0)); } ///Returns true if the window is focused - public @property bool active() @safe @nogc pure nothrow { + public @property bool active() @safe @nogc pure nothrow const { return flags & IS_ACTIVE; } ///Sets the IS_ACTIVE flag to the given value @@ -256,15 +257,34 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { } return active(); } + public @property bool resizableH() @safe @nogc pure nothrow const { + return flags & IS_RESIZABLE_BY_MOUSE_H ? true : false; + } + public @property bool resizableH(bool val) @safe @nogc pure nothrow { + if (val) flags |= IS_RESIZABLE_BY_MOUSE_H; + else flags &= ~IS_RESIZABLE_BY_MOUSE_H; + return flags & IS_RESIZABLE_BY_MOUSE_H ? true : false; + } + public @property bool resizableV() @safe @nogc pure nothrow const { + return flags & IS_RESIZABLE_BY_MOUSE_V ? true : false; + } + public @property bool resizableV(bool val) @safe @nogc pure nothrow { + if (val) flags |= IS_RESIZABLE_BY_MOUSE_V; + else flags &= ~IS_RESIZABLE_BY_MOUSE_V; + return flags & IS_RESIZABLE_BY_MOUSE_V ? true : false; + } + public @property bool isResized() @safe @nogc pure nothrow const { + return flags & IS_RESIZED ? true : false; + } ///Returns whether the window is moved or not - public @property bool isMoved() @safe @nogc pure nothrow { + public @property bool isMoved() @safe @nogc pure nothrow const { return flags & IS_MOVED ? true : false; } ///Sets whether the window is moved or not public @property bool isMoved(bool val) @safe @nogc pure nothrow { if (val) flags |= IS_MOVED; else flags &= ~IS_MOVED; - return isMoved(); + return flags & IS_MOVED ? true : false; } ///Sets the title of the window public void setTitle(Text s) @trusted { @@ -492,6 +512,7 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { public void passMCE(MouseEventCommons mec, MouseClickEvent mce) { if (!isMoved) { lastMousePos = Point(mce.x - position.left, mce.y - position.top); + //WindowElement block foreach (WindowElement we; elements) { if (we.getPosition.isBetween(lastMousePos)) { lastMouseEventTarget = we; @@ -501,6 +522,7 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { return; } } + //Header button block foreach (ISmallButton sb; smallButtons) { WindowElement we = cast(WindowElement)sb; if (we.getPosition.isBetween(lastMousePos)) { @@ -511,6 +533,61 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { return; } } + //Resize event block + if (resizableH) { + if (mce.x - 2 <= position.left) { ///Left edge + if (resizableV) { + if (mce.y - 2 <= position.top) {///Top-left corner + flags |= IS_RESIZED_T | IS_RESIZED_L; + handler.initDragEvent(this); + return; + } else if (mce.y >= position.bottom - 2) {///Bottom-left corner + flags |= IS_RESIZED_B | IS_RESIZED_L; + handler.initDragEvent(this); + return; + } + } + flags |= IS_RESIZED_L;///Left edge in general + handler.initDragEvent(this); + return; + } else if (mce.x >= position.right - 2) {///Right edge + if (resizableV) { + if (mce.y - 2 <= position.top) {///Top-right corner + flags |= IS_RESIZED_T | IS_RESIZED_R; + handler.initDragEvent(this); + return; + } else if (mce.y >= position.bottom - 2) {///Bottom-right corner + flags |= IS_RESIZED_B | IS_RESIZED_R; + handler.initDragEvent(this); + return; + } + } + flags |= IS_RESIZED_R;///Right edge in general + handler.initDragEvent(this); + return; + } else if (resizableV) {///Top or bottom edge + if (mce.y - 2 <= position.top) { + flags |= IS_RESIZED_T; + handler.initDragEvent(this); + return; + } else if(mce.y >= position.bottom - 2) { + flags |= IS_RESIZED_B; + handler.initDragEvent(this); + return; + } + } + } else if (resizableV) {///The previous one already took care of the corner cases, so we won't have to deal with them here + if (mce.y - 2 <= position.top) { + flags |= IS_RESIZED_T; + handler.initDragEvent(this); + return; + } else if(mce.y >= position.bottom - 2) { + flags |= IS_RESIZED_B; + handler.initDragEvent(this); + return; + } + } + //Move even block const int headerHeight = getStyleSheet().drawParameters["WindowHeaderHeight"]; if (lastMousePos.y < headerHeight) { isMoved = true; @@ -519,6 +596,7 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { lastMouseEventTarget = null; } else if (!mce.state) { isMoved = false; + flags &= ~IS_MOVED; } } ///Passes mouse move event @@ -529,6 +607,44 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { relMove(mme.relX, mme.relY); else isMoved = false; + } else if (isResized) { + const Box safePosition = position; + const int windowMinSize = getStyleSheet().drawParameters["windowMinimumSizes"]; + if (mme.buttonState) { + switch (flags & IS_RESIZED) { + case IS_RESIZED_L: + position.left += mme.relX; + break; + case IS_RESIZED_R: + position.right += mme.relX; + break; + case IS_RESIZED_T: + position.top += mme.relY; + break; + case IS_RESIZED_B: + position.bottom += mme.relY; + break; + case IS_RESIZED_L | IS_RESIZED_T: + position.left += mme.relX; + position.top += mme.relY; + break; + case IS_RESIZED_L | IS_RESIZED_B: + position.left += mme.relX; + position.bottom += mme.relY; + break; + case IS_RESIZED_R | IS_RESIZED_T: + position.right += mme.relX; + position.top += mme.relY; + break; + case IS_RESIZED_R | IS_RESIZED_B: + position.right += mme.relX; + position.bottom += mme.relY; + break; + default: break; + } + if (position.width < windowMinSize || position.height < windowMinSize) position = safePosition; + else draw(); + } else flags &= ~IS_RESIZED; } else if (lastMouseEventTarget) { mme.x = lastMousePos.x; mme.y = lastMousePos.y; @@ -546,6 +662,46 @@ public class Window : ElementContainer, Focusable, MouseEventReceptor { return; } } + if (!position.isBetween(mme.x, mme.y)) { + if (handler.getCursor != CursorType.Arrow) handler.resetCursor(); + } else { + if (resizableH) { + if (mme.x - 2 <= position.left) { ///Left edge + if (resizableV) { + if (mme.y - 2 <= position.top) {///Top-left corner + handler.setCursor(CursorType.ResizeNWSE); + return; + } else if (mme.y >= position.bottom - 2) {///Bottom-left corner + handler.setCursor(CursorType.ResizeNESW); + return; + } + } + handler.setCursor(CursorType.ResizeWE);///Left edge in general + return; + } else if (mme.x >= position.right - 2) {///Right edge + if (resizableV) { + if (mme.y - 2 <= position.top) {///Top-right corner + handler.setCursor(CursorType.ResizeNESW); + return; + } else if (mme.y >= position.bottom - 2) {///Bottom-right corner + handler.setCursor(CursorType.ResizeNWSE); + return; + } + } + handler.setCursor(CursorType.ResizeWE);///Right edge in general + return; + } else if ((mme.y - 2 <= position.top || mme.y >= position.bottom - 2) && resizableV) {///Top or bottom edge + handler.setCursor(CursorType.ResizeNS); + return; + } + } else if (resizableV) {///The previous one already took care of the corner cases, so we won't have to deal with them here + if (mme.y - 2 <= position.top || mme.y >= position.bottom - 2) {///Top or bottom edge + handler.setCursor(CursorType.ResizeNS); + return; + } + } + if (handler.getCursor != CursorType.Arrow) handler.resetCursor(); + } } } ///Passes mouse scroll event diff --git a/pixelperfectengine/src/pixelperfectengine/concrete/windowhandler.d b/pixelperfectengine/src/pixelperfectengine/concrete/windowhandler.d index 4635d5a..ea42c48 100644 --- a/pixelperfectengine/src/pixelperfectengine/concrete/windowhandler.d +++ b/pixelperfectengine/src/pixelperfectengine/concrete/windowhandler.d @@ -72,6 +72,9 @@ public class WindowHandler : InputListener, MouseListener, PopUpHandler { SDL_SetCursor(sdlCursor); return cursor; } + public void resetCursor() { + setCursor(CursorType.Arrow); + } /** * Returns the current cursor type. */ diff --git a/test3/app.d b/test3/app.d index 65a739c..802daa8 100644 --- a/test3/app.d +++ b/test3/app.d @@ -107,6 +107,7 @@ public class TestWindow : Window { Button btn_messageDialog; Button btn_addElem; Button btn_subMenu; + Button btn_resizeTest; VertScrollBar vScrollBarTest; Label singleLineLabel; Label multiLineLabel; @@ -161,6 +162,9 @@ public class TestWindow : Window { btn_subMenu = new Button("Submenu test", "", Box.bySize(300, 95, 70, 20)); btn_subMenu.onMouseLClick = &btn_subMenu_onClick; addElement(btn_subMenu); + btn_resizeTest = new Button("Window resize", "", Box.bySize(300, 120, 70, 20)); + btn_resizeTest.onMouseLClick = &btn_resizeTest_onClick; + addElement(btn_resizeTest); multiLineDialog = lang["multilinedialog"]; } @@ -191,6 +195,9 @@ public class TestWindow : Window { menutree[2] ~= new PopUpMenuElement("", "Submenu 3/3"); handler.addPopUpElement(new PopUpMenu(menutree, "", null)); } + private void btn_resizeTest_onClick(Event ev) { + handler.addWindow(new ResizableWindow()); + } private void fileDialogEvent(Event ev) { FileEvent fe = cast(FileEvent)ev; writeln(fe.path); @@ -200,4 +207,12 @@ public class TestWindow : Window { private void listView_onItemAdd(Event ev) { writeln(ev); } +} + +public class ResizableWindow : Window { + public this() { + resizableH = true; + resizableV = true; + super(Box.bySize(0, 0, 200, 200), "Resizing test"); + } } \ No newline at end of file