diff --git a/apps/file_manager/folder.cpp b/apps/file_manager/folder.cpp index 3ca5015..0d62cfe 100644 --- a/apps/file_manager/folder.cpp +++ b/apps/file_manager/folder.cpp @@ -56,6 +56,8 @@ EsError FSDirRenameItem(Folder *folder, String oldName, String newName) { EsError FSDirEnumerate(Folder *folder) { // TODO Recurse mode. + + EsSleep(2000); EsNodeType type; diff --git a/desktop/gui.cpp b/desktop/gui.cpp index ed3f221..dfb8966 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -85,6 +85,7 @@ EsTextStyle TextPlanGetPrimaryStyle(EsTextPlan *plan); EsElement *UIFindHoverElementRecursively(EsElement *element, int offsetX, int offsetY, EsPoint position); const EsStyle *UIGetDefaultStyleVariant(const EsStyle *style, EsElement *parent); void AccessKeysCenterHint(EsElement *element, EsMessage *message); +void UIRemoveFocusFromElement(EsElement *oldFocus); void InspectorSetup(EsWindow *window); void InspectorNotifyElementEvent(EsElement *element, const char *cCategory, const char *cFormat, ...); @@ -449,7 +450,7 @@ struct EsWindow : EsElement { EsElement *mainPanel, *toolbar; EsPanel *toolbarSwitcher; - EsElement *dialogOverlay, *dialogPanel; + EsElement *dialogWrapper; EsPoint mousePosition; @@ -710,67 +711,6 @@ int ProcessWindowBorderMessage(EsWindow *window, EsMessage *message, EsRectangle return ES_HANDLED; } -// --------------------------------- Dialogs. - -void EsDialogClose(EsWindow *window) { - EsMessageMutexCheck(); - EsAssert(window->hasDialog); // The window does not have an open dialog. - window->dialogPanel->Destroy(); - window->dialogOverlay->Destroy(); // TODO This looks bad if we immediately open a new dialog. But maybe it'll look alright with exiting transitions? - window->dialogOverlay = window->dialogPanel = nullptr; - window->children[0]->children[0]->flags &= ~ES_ELEMENT_BLOCK_FOCUS; - window->children[1]->state &= ~UI_STATE_BLOCK_INTERACTION; - window->hasDialog = false; -} - -EsElement *EsDialogShowAlert(EsWindow *window, const char *title, ptrdiff_t titleBytes, - const char *content, ptrdiff_t contentBytes, uint32_t iconID, uint32_t flags) { - EsElement *dialog = EsDialogShow(window); - if (!dialog) return nullptr; - EsPanel *heading = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, ES_STYLE_DIALOG_HEADING); - if (!heading) return nullptr; - - if (iconID) { - EsIconDisplayCreate(heading, ES_FLAGS_DEFAULT, {}, iconID); - } - - EsTextDisplayCreate(heading, ES_CELL_H_FILL | ES_CELL_V_CENTER, ES_STYLE_TEXT_HEADING2, - title, titleBytes)->cName = "dialog heading"; - EsTextDisplayCreate(EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_VERTICAL, ES_STYLE_DIALOG_CONTENT), - ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, - content, contentBytes)->cName = "dialog contents"; - - EsPanel *buttonArea = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL | ES_PANEL_REVERSE, ES_STYLE_DIALOG_BUTTON_AREA); - if (!buttonArea) return nullptr; - - if (flags & ES_DIALOG_ALERT_OK_BUTTON) { - EsButton *button = EsButtonCreate(buttonArea, ES_BUTTON_DEFAULT, 0, "OK"); - EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) { EsDialogClose(instance->window); }); - EsElementFocus(button); - } - - return buttonArea; -} - -EsElement *EsDialogShow(EsWindow *window) { - EsAssert(window->windowStyle == ES_WINDOW_NORMAL); // Can only show dialogs on normal windows. - EsAssert(!window->hasDialog); // Cannot nest dialogs. - - EsElement *mainStack = window->children[0]; - mainStack->children[0]->flags |= ES_ELEMENT_BLOCK_FOCUS; - window->children[1]->state |= UI_STATE_BLOCK_INTERACTION; - window->hasDialog = true; - window->dialogOverlay = EsPanelCreate(mainStack, ES_CELL_FILL, ES_STYLE_PANEL_MODAL_OVERLAY); - window->dialogOverlay->cName = "modal overlay"; - window->dialogPanel = EsPanelCreate(mainStack, ES_PANEL_VERTICAL | ES_CELL_CENTER | ES_CELL_SHRINK, ES_STYLE_DIALOG_SHADOW); - window->dialogPanel->cName = "dialog"; - - // EsElementStartTransition(window->dialogOverlay, ES_TRANSITION_FADE_IN, ES_FLAGS_DEFAULT, 3.0f); - // EsElementStartTransition(window->dialogPanel, ES_TRANSITION_FADE_IN, ES_FLAGS_DEFAULT, 3.0f); - - return window->dialogPanel; -} - // --------------------------------- Windows. void UIWindowNeedsUpdate(EsWindow *window) { @@ -1298,6 +1238,10 @@ void EsElementStartTransition(EsElement *element, EsTransitionType transitionTyp return; } + if (~element->state & UI_STATE_ENTERED) { + flags |= ES_ELEMENT_TRANSITION_ENTRANCE; + } + if (transitionType == ES_TRANSITION_FADE_IN) { flags |= ES_ELEMENT_TRANSITION_ENTRANCE; } else if (transitionType == ES_TRANSITION_FADE_OUT) { @@ -1680,6 +1624,9 @@ void ProcessAnimations() { if (element->transitionFlags & ES_ELEMENT_TRANSITION_HIDE_AFTER_COMPLETE) { EsElementSetHidden(element, true); } + + EsMessage m = { .type = ES_MSG_TRANSITION_COMPLETE }; + EsMessageSend(element, &m); } bool backgroundAnimationComplete = ThemeAnimationComplete(&element->animation); @@ -3553,6 +3500,94 @@ EsButton *EsPanelRadioGroupGetChecked(EsPanel *panel) { return (EsButton *) panel->GetChild(0); } +// --------------------------------- Dialogs. + +int ProcessDialogClosingMessage(EsElement *element, EsMessage *message) { + if (message->type == ES_MSG_TRANSITION_COMPLETE) { + // Destroy the dialog and its wrapper. + EsElementDestroy(EsElementGetLayoutParent(element)); + } + + return ProcessPanelMessage(element, message); +} + +void EsDialogClose(EsWindow *window) { + EsMessageMutexCheck(); + EsAssert(window->hasDialog); + + EsAssert(window->dialogWrapper->children[0]->messageClass == ProcessPanelMessage); + window->dialogWrapper->children[0]->messageClass = ProcessDialogClosingMessage; + EsElementStartTransition(window->dialogWrapper->children[0], ES_TRANSITION_ZOOM_OUT_LIGHT, ES_ELEMENT_TRANSITION_EXIT, 1.0f); + + window->dialogWrapper = nullptr; + window->children[0]->children[0]->state &= ~UI_STATE_BLOCK_INTERACTION; + window->children[1]->state &= ~UI_STATE_BLOCK_INTERACTION; + window->hasDialog = false; + + if (window->inactiveFocus) { + EsElementFocus(window->inactiveFocus, false); + window->inactiveFocus->Repaint(true); + window->inactiveFocus = nullptr; + } +} + +EsElement *EsDialogShowAlert(EsWindow *window, const char *title, ptrdiff_t titleBytes, + const char *content, ptrdiff_t contentBytes, uint32_t iconID, uint32_t flags) { + EsElement *dialog = EsDialogShow(window); + if (!dialog) return nullptr; + EsPanel *heading = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, ES_STYLE_DIALOG_HEADING); + if (!heading) return nullptr; + + if (iconID) { + EsIconDisplayCreate(heading, ES_FLAGS_DEFAULT, {}, iconID); + } + + EsTextDisplayCreate(heading, ES_CELL_H_FILL | ES_CELL_V_CENTER, ES_STYLE_TEXT_HEADING2, + title, titleBytes)->cName = "dialog heading"; + EsTextDisplayCreate(EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_VERTICAL, ES_STYLE_DIALOG_CONTENT), + ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, + content, contentBytes)->cName = "dialog contents"; + + EsPanel *buttonArea = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL | ES_PANEL_REVERSE, ES_STYLE_DIALOG_BUTTON_AREA); + if (!buttonArea) return nullptr; + + if (flags & ES_DIALOG_ALERT_OK_BUTTON) { + EsButton *button = EsButtonCreate(buttonArea, ES_BUTTON_DEFAULT, 0, "OK"); + EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) { EsDialogClose(instance->window); }); + EsElementFocus(button); + } + + return buttonArea; +} + +EsElement *EsDialogShow(EsWindow *window) { + // TODO Show on a separate window? + // TODO Maybe allow nested dialogs? + + EsAssert(window->windowStyle == ES_WINDOW_NORMAL); // Can only show dialogs on normal windows. + EsAssert(!window->hasDialog); // Cannot nest dialogs. + + if (window->focused) { + window->inactiveFocus = window->focused; + window->inactiveFocus->Repaint(true); + UIRemoveFocusFromElement(window->focused); + window->focused = nullptr; + } + + EsElement *mainStack = window->children[0]; + mainStack->children[0]->state |= UI_STATE_BLOCK_INTERACTION; // Main content. + window->children[1]->state |= UI_STATE_BLOCK_INTERACTION; // Toolbar. + + window->hasDialog = true; + window->dialogWrapper = EsPanelCreate(mainStack, ES_PANEL_VERTICAL | ES_CELL_FILL, ES_STYLE_DIALOG_WRAPPER); + window->dialogWrapper->cName = "dialog wrapper"; + EsPanel *dialog = EsPanelCreate(window->dialogWrapper, ES_PANEL_VERTICAL | ES_CELL_SHRINK, ES_STYLE_DIALOG_SHADOW); + dialog->cName = "dialog"; + EsElementStartTransition(dialog, ES_TRANSITION_ZOOM_OUT_LIGHT, ES_FLAGS_DEFAULT, 1.0f); + + return dialog; +} + // --------------------------------- Canvas panes. struct EsCanvasPane : EsElement { diff --git a/desktop/os.header b/desktop/os.header index 1bd49b3..4b3131a 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -941,6 +941,7 @@ enum EsMessageType { ES_MSG_MOUSE_MIDDLE_DRAG = 0x2028 // Similar to LEFT_DRAG above, but for the middle button. ES_MSG_GET_ACCESS_KEY_HINT_BOUNDS = 0x2029 // Get the bounds to display an access key hint. ES_MSG_UI_SCALE_CHANGED = 0x202A // The UI scale has changed. + ES_MSG_TRANSITION_COMPLETE = 0x202B // The transition started with EsElementStartTransition completed. // State change messages: (causes a style refresh) ES_MSG_STATE_CHANGE_MESSAGE_START = 0x2080 diff --git a/desktop/styles.header b/desktop/styles.header index 0050614..0608bea 100644 --- a/desktop/styles.header +++ b/desktop/styles.header @@ -22,6 +22,7 @@ define ES_STYLE_DIALOG_BUTTON_AREA (ES_STYLE_CAST(1259)) define ES_STYLE_DIALOG_CONTENT (ES_STYLE_CAST(1261)) define ES_STYLE_DIALOG_HEADING (ES_STYLE_CAST(1263)) define ES_STYLE_DIALOG_SHADOW (ES_STYLE_CAST(1311)) +private define ES_STYLE_DIALOG_WRAPPER (ES_STYLE_CAST(1665)) private define ES_STYLE_DOUBLE_CLICK_TEST (ES_STYLE_CAST(1585)) define ES_STYLE_ICON_DISPLAY (ES_STYLE_CAST(1265)) define ES_STYLE_ICON_DISPLAY_SMALL (ES_STYLE_CAST(1543)) @@ -61,7 +62,6 @@ private define ES_STYLE_PANEL_INSPECTOR_WINDOW_ROOT (ES_STYLE_CAST(1319)) private define ES_STYLE_PANEL_MENU_COLUMN (ES_STYLE_CAST(1321)) private define ES_STYLE_PANEL_MENU_CONTAINER (ES_STYLE_CAST(1323)) private define ES_STYLE_PANEL_MENU_ROOT (ES_STYLE_CAST(1325)) -private define ES_STYLE_PANEL_MODAL_OVERLAY (ES_STYLE_CAST(1327)) define ES_STYLE_PANEL_POPUP (ES_STYLE_CAST(1331)) define ES_STYLE_PANEL_SHEET (ES_STYLE_CAST(1333)) private define ES_STYLE_PANEL_SHUTDOWN_OVERLAY (ES_STYLE_CAST(1335)) diff --git a/res/Theme Source.dat b/res/Theme Source.dat index 794062c..a35ebe1 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 65f8991..8c2faf6 100644 Binary files a/res/Themes/Theme.dat and b/res/Themes/Theme.dat differ