diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 2b05875..91b99a1 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -464,9 +464,10 @@ void WindowTabDestroy(WindowTab *tab) { } } -void WindowTabMoveToNewContainer(WindowTab *tab) { +WindowTab *WindowTabMoveToNewContainer(WindowTab *tab) { // Create the new tab and container window. WindowTab *newTab = WindowTabCreate(ContainerWindowCreate()); + if (!newTab) return nullptr; // Move ownership of the instance to the new tab. newTab->applicationInstance = tab->applicationInstance; @@ -479,6 +480,7 @@ void WindowTabMoveToNewContainer(WindowTab *tab) { // Destroy the old tab, and activate the new one. WindowTabDestroy(tab); // Deplaces the embedded window from the old container. WindowTabActivate(newTab); + return newTab; } int ContainerWindowMessage(EsElement *element, EsMessage *message) { @@ -582,14 +584,59 @@ int WindowTabMessage(EsElement *element, EsMessage *message) { message->animate.complete = ReorderItemAnimate(tab, message->animate.deltaMs, "windowTabEntranceDuration"); } else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) { } else if (message->type == ES_MSG_MOUSE_LEFT_DRAG) { + EsElementSetDisabled(band->GetChild(0), true); + if (band->items.Length() == 1) { + // TODO Dragging into other containers. EsPoint screenPosition = EsMouseGetPosition(); WindowChangeBounds(RESIZE_MOVE, screenPosition.x, screenPosition.y, &gui.lastClickX, &gui.lastClickY, band->window); } else { - ReorderItemDragged(tab, message->mouseDragged.newPositionX); - } + EsRectangle tabBarBounds = tab->parent->GetWindowBounds(); + EsRectangle tabBounds = tab->GetWindowBounds(); + int32_t mouseX = message->mouseDragged.newPositionX + tabBounds.l; + int32_t mouseY = message->mouseDragged.newPositionY + tabBounds.t; + int32_t dragOffThreshold = GetConstantNumber("tabDragOffThreshold"); - EsElementSetDisabled(band->GetChild(0), true); + if (EsRectangleContains(EsRectangleAdd(tabBarBounds, ES_RECT_1I(-dragOffThreshold)), mouseX, mouseY)) { + ReorderItemDragged(tab, message->mouseDragged.newPositionX); + } else { + // Save information about the old container. + int32_t oldTabDragX = mouseX - tab->dragOffset; + int32_t oldTabDragY = mouseY - (gui.lastClickY - tab->offsetY); + EsRectangle oldContainerBounds = tab->window->GetScreenBounds(); + EsRectangle oldTabBarScreenBounds = tab->parent->GetScreenBounds(); + EsWindow *oldContainer = tab->window; + EsPoint mousePosition = EsMouseGetPosition(oldContainer); + + // End the drag on this container. + EsMessage m = { .type = ES_MSG_MOUSE_LEFT_UP }; + UIMouseUp(oldContainer, &m, false); + + // Move the tab to a new container. + WindowTab *newTab = WindowTabMoveToNewContainer(tab); + + if (newTab) { + // Work out the position of the new container, so that the mouse position within the tab is preserved. + newTab->window->width = Width(oldContainerBounds); + newTab->window->height = Height(oldContainerBounds); + UIWindowLayoutNow(newTab->window, nullptr); + EsRectangle newTabWindowBounds = newTab->GetWindowBounds(); + EsRectangle bounds = ES_RECT_4PD(oldTabBarScreenBounds.l + oldTabDragX - newTabWindowBounds.l, + oldTabBarScreenBounds.t + oldTabDragY - newTabWindowBounds.t, + Width(oldContainerBounds), Height(oldContainerBounds)); + EsSyscall(ES_SYSCALL_WINDOW_MOVE, newTab->window->handle, (uintptr_t) &bounds, 0, ES_WINDOW_MOVE_DYNAMIC); + + // Start the drag on the new container. + EsSyscall(ES_SYSCALL_WINDOW_TRANSFER_PRESS, oldContainer->handle, newTab->window->handle, 0, 0); + newTab->window->pressed = newTab; + newTab->window->dragged = newTab; + gui.mouseButtonDown = true; + gui.draggingStarted = true; + gui.lastClickX = mousePosition.x + oldContainerBounds.l - bounds.l; + gui.lastClickY = mousePosition.y + oldContainerBounds.t - bounds.t; + } + } + } } else if (message->type == ES_MSG_MOUSE_LEFT_UP) { ReorderItemDragComplete(tab); EsElementSetDisabled(band->GetChild(0), false); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 40a6b6f..2544684 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -79,7 +79,7 @@ EsElement *WindowGetMainPanel(EsWindow *window); int AccessKeyLayerMessage(EsElement *element, EsMessage *message); void AccessKeyModeExit(); int ProcessButtonMessage(EsElement *element, EsMessage *message); -void UIMousePressReleased(EsWindow *window, EsMessage *message, bool sendClick); +void UIMouseUp(EsWindow *window, EsMessage *message, bool sendClick); void UIMaybeRemoveFocusedElement(EsWindow *window); EsTextStyle TextPlanGetPrimaryStyle(EsTextPlan *plan); EsElement *UIFindHoverElementRecursively(EsElement *element, int offsetX, int offsetY, EsPoint position); @@ -860,7 +860,7 @@ EsWindow *EsWindowCreate(EsInstance *instance, EsWindowStyle style) { EsMessageMutexCheck(); for (uintptr_t i = 0; i < gui.allWindows.Length(); i++) { - UIMousePressReleased(gui.allWindows[i], nullptr, false); + UIMouseUp(gui.allWindows[i], nullptr, false); } EsWindow *window = (EsWindow *) EsHeapAllocate(sizeof(EsWindow), true); @@ -6063,7 +6063,67 @@ int UIMessageSendPropagateToAncestors(EsElement *element, EsMessage *message, Es return 0; } -void UIMousePressReleased(EsWindow *window, EsMessage *message, bool sendClick) { +void UIMouseDown(EsWindow *window, EsMessage *message) { + window->mousePosition.x = message->mouseDown.positionX; + window->mousePosition.y = message->mouseDown.positionY; + + AccessKeyModeExit(); + + double timeStampMs = EsTimeStampMs(); + + if (gui.clickChainStartMs + api.global->clickChainTimeoutMs < timeStampMs + || window->hovered != gui.clickChainElement) { + // Start a new click chain. + gui.clickChainStartMs = timeStampMs; + gui.clickChainCount = 1; + gui.clickChainElement = window->hovered; + } else { + gui.clickChainStartMs = timeStampMs; + gui.clickChainCount++; + } + + message->mouseDown.clickChainCount = gui.clickChainCount; + + gui.lastClickX = message->mouseDown.positionX; + gui.lastClickY = message->mouseDown.positionY; + gui.lastClickButton = message->type; + gui.mouseButtonDown = true; + + if ((~window->hovered->flags & ES_ELEMENT_DISABLED) && (~window->hovered->state & UI_STATE_BLOCK_INTERACTION)) { + // If the hovered element is destroyed in response to one of these messages, + // window->hovered will be set to nullptr, so save the element here. + EsElement *element = window->hovered; + + if (message->type == ES_MSG_MOUSE_LEFT_DOWN) { + element->state |= UI_STATE_LEFT_PRESSED; + } + + window->pressed = element; + EsMessage m = { ES_MSG_PRESSED_START }; + EsMessageSend(element, &m); + + EsRectangle bounds = element->GetWindowBounds(); + message->mouseDown.positionX -= bounds.l; + message->mouseDown.positionY -= bounds.t; + + if (ES_REJECTED != UIMessageSendPropagateToAncestors(element, message, &window->dragged)) { + if (window->dragged && (~window->dragged->flags & ES_ELEMENT_NO_FOCUS_ON_CLICK)) { + EsElementFocus(window->dragged, false); + } + } + } + + if (window->hovered != window->focused && window->focused && (~window->focused->state & UI_STATE_LOST_STRONG_FOCUS)) { + EsMessage m = { ES_MSG_STRONG_FOCUS_END }; + window->focused->state |= UI_STATE_LOST_STRONG_FOCUS; + EsMessageSend(window->focused, &m); + } +} + +void UIMouseUp(EsWindow *window, EsMessage *message, bool sendClick) { + gui.mouseButtonDown = false; + window->dragged = nullptr; + if (window->pressed) { EsElement *pressed = window->pressed; window->pressed = nullptr; @@ -6447,7 +6507,7 @@ void UIHandleKeyMessage(EsWindow *window, EsMessage *message) { if (window->pressed) { if (message->keyboard.scancode == ES_SCANCODE_ESCAPE) { - UIMousePressReleased(window, nullptr, false); + UIMouseUp(window, nullptr, false); return; } } @@ -6787,71 +6847,16 @@ void UIProcessWindowManagerMessage(EsWindow *window, EsMessage *message, Process } else if (message->type == ES_MSG_MOUSE_EXIT) { window->hovering = false; } else if (message->type == ES_MSG_MOUSE_LEFT_DOWN || message->type == ES_MSG_MOUSE_RIGHT_DOWN || message->type == ES_MSG_MOUSE_MIDDLE_DOWN) { - window->mousePosition.x = message->mouseDown.positionX; - window->mousePosition.y = message->mouseDown.positionY; - - AccessKeyModeExit(); - if (gui.mouseButtonDown || window->targetMenu) { goto skipInputMessage; } - double timeStampMs = EsTimeStampMs(); - - if (gui.clickChainStartMs + api.global->clickChainTimeoutMs < timeStampMs - || window->hovered != gui.clickChainElement) { - // Start a new click chain. - gui.clickChainStartMs = timeStampMs; - gui.clickChainCount = 1; - gui.clickChainElement = window->hovered; - } else { - gui.clickChainStartMs = timeStampMs; - gui.clickChainCount++; - } - - message->mouseDown.clickChainCount = gui.clickChainCount; - - gui.lastClickX = message->mouseDown.positionX; - gui.lastClickY = message->mouseDown.positionY; - gui.lastClickButton = message->type; - gui.mouseButtonDown = true; - - if ((~window->hovered->flags & ES_ELEMENT_DISABLED) && (~window->hovered->state & UI_STATE_BLOCK_INTERACTION)) { - // If the hovered element is destroyed in response to one of these messages, - // window->hovered will be set to nullptr, so save the element here. - EsElement *element = window->hovered; - - if (message->type == ES_MSG_MOUSE_LEFT_DOWN) { - element->state |= UI_STATE_LEFT_PRESSED; - } - - window->pressed = element; - EsMessage m = { ES_MSG_PRESSED_START }; - EsMessageSend(element, &m); - - EsRectangle bounds = element->GetWindowBounds(); - message->mouseDown.positionX -= bounds.l; - message->mouseDown.positionY -= bounds.t; - - if (ES_REJECTED != UIMessageSendPropagateToAncestors(element, message, &window->dragged)) { - if (window->dragged && (~window->dragged->flags & ES_ELEMENT_NO_FOCUS_ON_CLICK)) { - EsElementFocus(window->dragged, false); - } - } - } - - if (window->hovered != window->focused && window->focused && (~window->focused->state & UI_STATE_LOST_STRONG_FOCUS)) { - EsMessage m = { ES_MSG_STRONG_FOCUS_END }; - window->focused->state |= UI_STATE_LOST_STRONG_FOCUS; - EsMessageSend(window->focused, &m); - } + UIMouseDown(window, message); } else if (message->type == ES_MSG_MOUSE_LEFT_UP || message->type == ES_MSG_MOUSE_RIGHT_UP || message->type == ES_MSG_MOUSE_MIDDLE_UP) { AccessKeyModeExit(); if (gui.mouseButtonDown && gui.lastClickButton == message->type - 1) { - gui.mouseButtonDown = false; - window->dragged = nullptr; - UIMousePressReleased(window, message, true); + UIMouseUp(window, message, true); } } else if (message->type == ES_MSG_KEY_UP || message->type == ES_MSG_KEY_DOWN) { UIHandleKeyMessage(window, message); diff --git a/desktop/os.header b/desktop/os.header index 01d78fc..8076297 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -807,6 +807,7 @@ enum EsSyscallType { ES_SYSCALL_WINDOW_CLOSE ES_SYSCALL_WINDOW_REDRAW ES_SYSCALL_WINDOW_MOVE + ES_SYSCALL_WINDOW_TRANSFER_PRESS ES_SYSCALL_WINDOW_GET_ID ES_SYSCALL_WINDOW_GET_BOUNDS ES_SYSCALL_WINDOW_GET_EMBED_KEYBOARD diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 28c14de..e2f0d5f 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -1132,6 +1132,25 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_WINDOW_MOVE) { SYSCALL_RETURN(success ? ES_SUCCESS : ES_ERROR_INVALID_DIMENSIONS, false); } +SYSCALL_IMPLEMENT(ES_SYSCALL_WINDOW_TRANSFER_PRESS) { + KObject _oldWindow(currentProcess, argument0, KERNEL_OBJECT_WINDOW); + CHECK_OBJECT(_oldWindow); + Window *oldWindow = (Window *) _oldWindow.object; + KObject _newWindow(currentProcess, argument1, KERNEL_OBJECT_WINDOW); + CHECK_OBJECT(_newWindow); + Window *newWindow = (Window *) _newWindow.object; + + KMutexAcquire(&windowManager.mutex); + + if (windowManager.pressedWindow == oldWindow) { + windowManager.pressedWindow = newWindow; + } + + KMutexRelease(&windowManager.mutex); + + SYSCALL_RETURN(ES_SUCCESS, false); +} + SYSCALL_IMPLEMENT(ES_SYSCALL_CURSOR_POSITION_GET) { EsPoint point = ES_POINT(windowManager.cursorX, windowManager.cursorY); SYSCALL_WRITE(argument0, &point, sizeof(EsPoint)); diff --git a/res/Theme Source.dat b/res/Theme Source.dat index 55aca07..d000f4a 100644 Binary files a/res/Theme Source.dat and b/res/Theme Source.dat differ diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat index def7e34..2e8c17e 100644 Binary files a/res/Themes/Theme.dat and b/res/Themes/Theme.dat differ