simpler dialog API

This commit is contained in:
nakst 2021-09-26 10:18:13 +01:00
parent f812789031
commit 194bc6dd73
9 changed files with 86 additions and 81 deletions

View File

@ -180,10 +180,10 @@ void SpawnTile() {
if (!MoveTiles(-1, 0, true) && !MoveTiles(1, 0, true) && !MoveTiles(0, -1, true) && !MoveTiles(0, 1, true)) {
// No moves are possible.
if (highScore < score) {
EsDialogShowAlert(instance->window, INTERFACE_STRING(Game2048GameOver), INTERFACE_STRING(Game2048NewHighScore),
EsDialogShow(instance->window, INTERFACE_STRING(Game2048GameOver), INTERFACE_STRING(Game2048NewHighScore),
ES_ICON_DIALOG_INFORMATION, ES_DIALOG_ALERT_OK_BUTTON);
} else {
EsDialogShowAlert(instance->window, INTERFACE_STRING(Game2048GameOver), INTERFACE_STRING(Game2048GameOverExplanation),
EsDialogShow(instance->window, INTERFACE_STRING(Game2048GameOver), INTERFACE_STRING(Game2048GameOverExplanation),
ES_ICON_DIALOG_INFORMATION, ES_DIALOG_ALERT_OK_BUTTON);
}
}

View File

@ -128,6 +128,7 @@ struct Instance : EsInstance {
EsTextbox *breadcrumbBar;
EsButton *newFolderButton;
EsTextDisplay *status;
EsDialog *blockingDialog;
union {
struct {
@ -312,7 +313,8 @@ void BlockingTaskThread(EsGeneric _instance) {
void BlockingTaskComplete(Instance *instance) {
EsAssert(instance->blockingTaskInProgress); // Task should have been in progress.
instance->blockingTaskInProgress = false;
if (instance->blockingTaskReachedTimeout) EsDialogClose(instance->window);
if (instance->blockingTaskReachedTimeout) EsDialogClose(instance->blockingDialog);
instance->blockingDialog = nullptr;
Task *task = &instance->blockingTask;
if (task->then) task->then(instance, task);
}
@ -331,7 +333,7 @@ void BlockingTaskQueue(Instance *instance, Task task) {
if (result == ES_ERROR_TIMEOUT_REACHED) {
instance->blockingTaskReachedTimeout = true;
EsDialogShowAlert(instance->window, task.cDescription, -1, INTERFACE_STRING(FileManagerOngoingTaskDescription), ES_ICON_TOOLS_TIMER_SYMBOLIC);
EsDialogShow(instance->window, task.cDescription, -1, INTERFACE_STRING(FileManagerOngoingTaskDescription), ES_ICON_TOOLS_TIMER_SYMBOLIC);
// TODO Progress bar; cancelling tasks.
} else {
instance->blockingTaskReachedTimeout = false;

View File

@ -799,7 +799,7 @@ int ListCallback(EsElement *element, EsMessage *message) {
EsApplicationStart(&request);
StringDestroy(&path);
} else {
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileManagerOpenFileError),
EsDialogShow(instance->window, INTERFACE_STRING(FileManagerOpenFileError),
INTERFACE_STRING(FileManagerNoRegisteredApplicationsForFile),
ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}
@ -1100,7 +1100,7 @@ void InstanceReportError(Instance *instance, int error, EsError code) {
message = interfaceString_FileManagerPermissionNotGrantedError;
}
EsDialogShowAlert(instance->window, errorTypeStrings[error], -1, message, -1,
EsDialogShow(instance->window, errorTypeStrings[error], -1, message, -1,
ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}

View File

@ -257,7 +257,7 @@ void NetworkingThread(EsGeneric argument) {
EsMessageMutexAcquire();
if (errorMessageBytes) {
EsDialogShowAlert(instance->window, EsLiteral("Connection failed"), errorMessage, errorMessageBytes,
EsDialogShow(instance->window, EsLiteral("Connection failed"), errorMessage, errorMessageBytes,
ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}

View File

@ -153,7 +153,7 @@ void InitialiseInstance(EsInstance *instance) {
EsButtonCreate(panel, ES_BUTTON_CHECKBOX, 0, "Checkbox");
EsButtonOnCommand(EsButtonCreate(panel, ES_FLAGS_DEFAULT, 0, "Alert 1"), [] (EsInstance *, EsElement *element, EsCommand *) {
EsDialogShowAlert(element->window, "Title", -1, "Content.", -1, ES_ICON_DIALOG_WARNING, ES_DIALOG_ALERT_OK_BUTTON);
EsDialogShow(element->window, "Title", -1, "Content.", -1, ES_ICON_DIALOG_WARNING, ES_DIALOG_ALERT_OK_BUTTON);
});
EsPanel *table = EsPanelCreate(panel, ES_CELL_H_FILL | ES_PANEL_TABLE | ES_PANEL_HORIZONTAL | ES_PANEL_TABLE_H_JUSTIFY);

View File

@ -681,18 +681,10 @@ void InstanceClose(EsInstance *instance) {
apiInstance->applicationNameBytes, apiInstance->applicationName);
}
// NOTE Duplicated from EsDialogShowAlert.
// TODO Is there a good way to make more modular dialogs?
EsElement *dialog = EsDialogShow(instance->window);
EsPanel *heading = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, ES_STYLE_DIALOG_HEADING);
EsIconDisplayCreate(heading, ES_FLAGS_DEFAULT, 0, ES_ICON_DIALOG_WARNING);
EsTextDisplayCreate(heading, ES_CELL_H_FILL | ES_CELL_V_CENTER, ES_STYLE_TEXT_HEADING2, cTitle);
EsPanel *contentArea = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_VERTICAL, ES_STYLE_DIALOG_CONTENT);
EsTextDisplayCreate(contentArea, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, content, contentBytes);
EsPanel *buttonArea = EsPanelCreate(dialog, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL | ES_PANEL_REVERSE, ES_STYLE_DIALOG_BUTTON_AREA);
EsDialog *dialog = EsDialogShow(instance->window, cTitle, -1, content, contentBytes, ES_ICON_DIALOG_WARNING);
if (!apiInstance->startupInformation->filePathBytes) {
EsPanel *row = EsPanelCreate(contentArea, ES_PANEL_HORIZONTAL, ES_STYLE_PANEL_FORM_TABLE);
EsPanel *row = EsPanelCreate(dialog->contentArea, ES_PANEL_HORIZONTAL, ES_STYLE_PANEL_FORM_TABLE);
EsTextDisplayCreate(row, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_LABEL, INTERFACE_STRING(FileCloseNewName));
EsTextbox *textbox = EsTextboxCreate(row);
EsInstanceClassEditorSettings *editorSettings = &apiInstance->editorSettings;
@ -702,23 +694,18 @@ void InstanceClose(EsInstance *instance) {
apiInstance->fileMenuNameTextbox = textbox;
}
EsButton *button;
button = EsButtonCreate(buttonArea, ES_BUTTON_CANCEL, 0, INTERFACE_STRING(CommonCancel));
EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) {
EsDialogClose(instance->window);
EsDialogAddButton(dialog, ES_BUTTON_CANCEL, 0, INTERFACE_STRING(CommonCancel),
[] (EsInstance *instance, EsElement *, EsCommand *) {
EsDialogClose(instance->window->dialogs.Last());
});
button = EsButtonCreate(buttonArea, ES_FLAGS_DEFAULT, ES_STYLE_PUSH_BUTTON_DANGEROUS, INTERFACE_STRING(FileCloseWithModificationsDelete));
EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) {
EsDialogAddButton(dialog, ES_FLAGS_DEFAULT, ES_STYLE_PUSH_BUTTON_DANGEROUS, INTERFACE_STRING(FileCloseWithModificationsDelete),
[] (EsInstance *instance, EsElement *, EsCommand *) {
EsInstanceDestroy(instance);
});
button = EsButtonCreate(buttonArea, ES_BUTTON_DEFAULT, 0, INTERFACE_STRING(FileCloseWithModificationsSave));
EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) {
EsButton *button = EsDialogAddButton(dialog, ES_BUTTON_DEFAULT, 0, INTERFACE_STRING(FileCloseWithModificationsSave),
[] (EsInstance *instance, EsElement *, EsCommand *) {
APIInstance *apiInstance = (APIInstance *) instance->_private;
if (apiInstance->startupInformation->filePathBytes) {
@ -1169,7 +1156,7 @@ EsMessage *EsMessageReceive() {
} break;
}
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotRename),
EsDialogShow(instance->window, INTERFACE_STRING(FileCannotRename),
errorMessage, errorMessageBytes, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}
@ -1313,7 +1300,7 @@ void EsInstanceOpenComplete(EsMessage *message, bool success, const char *errorT
if (!success || message->instanceOpen.file->error != ES_SUCCESS) {
if (errorTextBytes) {
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotOpen),
EsDialogShow(instance->window, INTERFACE_STRING(FileCannotOpen),
errorText, errorTextBytes,
ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
} else {
@ -1331,7 +1318,7 @@ void EsInstanceOpenComplete(EsMessage *message, bool success, const char *errorT
break;
}
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotOpen),
EsDialogShow(instance->window, INTERFACE_STRING(FileCannotOpen),
errorMessage, -1, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}
@ -1428,7 +1415,7 @@ void EsInstanceSaveComplete(EsMessage *message, bool success) {
} break;
}
EsDialogShowAlert(instance->window, INTERFACE_STRING(FileCannotSave),
EsDialogShow(instance->window, INTERFACE_STRING(FileCannotSave),
errorMessage, errorMessageBytes, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
}
}

View File

@ -453,7 +453,7 @@ struct EsWindow : EsElement {
EsElement *mainPanel, *toolbar;
EsPanel *toolbarSwitcher;
Array<EsElement *> dialogs;
Array<EsDialog *> dialogs;
EsPoint mousePosition;
@ -764,6 +764,7 @@ void UIWindowDestroy(EsWindow *window) {
EsSyscall(ES_SYSCALL_WINDOW_CLOSE, window->handle, 0, 0, 0);
EsHandleClose(window->handle);
window->checkVisible.Free();
EsAssert(!window->dialogs.Length());
window->dialogs.Free();
window->handle = ES_INVALID_HANDLE;
}
@ -3551,6 +3552,12 @@ EsButton *EsPanelRadioGroupGetChecked(EsPanel *panel) {
// --------------------------------- Dialogs.
struct EsDialog {
EsElement *mainPanel;
EsElement *buttonArea;
EsElement *contentArea;
};
int ProcessDialogClosingMessage(EsElement *element, EsMessage *message) {
if (message->type == ES_MSG_TRANSITION_COMPLETE) {
// Destroy the dialog and its wrapper.
@ -3560,17 +3567,19 @@ int ProcessDialogClosingMessage(EsElement *element, EsMessage *message) {
return ProcessPanelMessage(element, message);
}
void EsDialogClose(EsWindow *window) {
void EsDialogClose(EsDialog *dialog) {
EsMessageMutexCheck();
EsAssert(window->dialogs.Length());
EsElement *dialog = window->dialogs.Pop();
EsWindow *window = dialog->mainPanel->window;
bool isTop = window->dialogs.Last() == dialog;
window->dialogs.FindAndDelete(dialog, true);
EsAssert(dialog->messageClass == ProcessPanelMessage);
dialog->messageClass = ProcessDialogClosingMessage;
EsElementStartTransition(dialog, ES_TRANSITION_ZOOM_OUT_LIGHT, ES_ELEMENT_TRANSITION_EXIT, 1.0f);
EsAssert(dialog->mainPanel->messageClass == ProcessPanelMessage);
dialog->mainPanel->messageClass = ProcessDialogClosingMessage;
EsElementStartTransition(dialog->mainPanel, ES_TRANSITION_ZOOM_OUT_LIGHT, ES_ELEMENT_TRANSITION_EXIT, 1.0f);
if (!window->dialogs.Length()) {
if (!isTop) {
} else if (!window->dialogs.Length()) {
window->children[0]->children[0]->state &= ~UI_STATE_BLOCK_INTERACTION;
window->children[1]->state &= ~UI_STATE_BLOCK_INTERACTION;
@ -3580,43 +3589,18 @@ void EsDialogClose(EsWindow *window) {
window->inactiveFocus = nullptr;
}
} else {
window->dialogs.Last()->state &= ~UI_STATE_BLOCK_INTERACTION;
window->dialogs.Last()->mainPanel->state &= ~UI_STATE_BLOCK_INTERACTION;
}
}
EsElement *EsDialogShowAlert(EsWindow *window, const char *title, ptrdiff_t titleBytes,
EsDialog *EsDialogShow(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, 0, 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 | ES_BUTTON_CANCEL, 0, INTERFACE_STRING(CommonOK));
EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) { EsDialogClose(instance->window); });
EsElementFocus(button);
}
return buttonArea;
}
EsElement *EsDialogShow(EsWindow *window) {
// TODO Show on a separate window?
// TODO Support dialogs owned by other processes.
EsDialog *dialog = (EsDialog *) EsHeapAllocate(sizeof(EsDialog), true);
if (!dialog) return nullptr;
EsAssert(window->windowStyle == ES_WINDOW_NORMAL); // Can only show dialogs on normal windows.
if (window->focused) {
@ -3632,19 +3616,49 @@ EsElement *EsDialogShow(EsWindow *window) {
mainStack->children[0]->state |= UI_STATE_BLOCK_INTERACTION; // Main content.
window->children[1]->state |= UI_STATE_BLOCK_INTERACTION; // Toolbar.
} else {
window->dialogs.Last()->state |= UI_STATE_BLOCK_INTERACTION;
window->dialogs.Last()->mainPanel->state |= UI_STATE_BLOCK_INTERACTION;
}
EsElement *wrapper = EsPanelCreate(mainStack, ES_PANEL_VERTICAL | ES_CELL_FILL, ES_STYLE_DIALOG_WRAPPER);
wrapper->cName = "dialog wrapper";
EsPanel *dialog = EsPanelCreate(wrapper, 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);
dialog->mainPanel = EsPanelCreate(wrapper, ES_PANEL_VERTICAL | ES_CELL_SHRINK, ES_STYLE_DIALOG_SHADOW);
dialog->mainPanel->cName = "dialog";
EsElementStartTransition(dialog->mainPanel, ES_TRANSITION_ZOOM_OUT_LIGHT, ES_FLAGS_DEFAULT, 1.0f);
window->dialogs.Add(dialog);
EsElement *mainPanel = dialog->mainPanel;
EsPanel *heading = EsPanelCreate(mainPanel, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, ES_STYLE_DIALOG_HEADING);
if (iconID) {
EsIconDisplayCreate(heading, ES_FLAGS_DEFAULT, 0, iconID);
}
EsTextDisplayCreate(heading, ES_CELL_H_FILL | ES_CELL_V_CENTER, ES_STYLE_TEXT_HEADING2, title, titleBytes)->cName = "dialog heading";
dialog->contentArea = EsPanelCreate(mainPanel, ES_CELL_H_FILL | ES_PANEL_VERTICAL, ES_STYLE_DIALOG_CONTENT);
EsTextDisplayCreate(dialog->contentArea, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, content, contentBytes)->cName = "dialog contents";
EsPanel *buttonArea = EsPanelCreate(mainPanel, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL | ES_PANEL_REVERSE, ES_STYLE_DIALOG_BUTTON_AREA);
dialog->buttonArea = buttonArea;
if (flags & ES_DIALOG_ALERT_OK_BUTTON) {
EsButton *button = EsButtonCreate(buttonArea, ES_BUTTON_DEFAULT | ES_BUTTON_CANCEL, 0, INTERFACE_STRING(CommonOK));
EsElementFocus(button);
EsButtonOnCommand(button, [] (EsInstance *instance, EsElement *, EsCommand *) {
EsDialogClose(instance->window->dialogs.Last());
});
}
return dialog;
}
EsButton *EsDialogAddButton(EsDialog *dialog, uint64_t flags, EsStyle *style, const char *label, ptrdiff_t labelBytes, EsCommandCallback callback) {
EsButton *button = EsButtonCreate(dialog->buttonArea, flags, style, label, labelBytes);
if (button) EsButtonOnCommand(button, callback);
return button;
}
// --------------------------------- Canvas panes.
struct EsCanvasPane : EsElement {

View File

@ -21,6 +21,7 @@ opaque_type EsHeap none;
opaque_type EsFileStore none;
opaque_type EsUserTask none;
opaque_type EsBundle none;
opaque_type EsDialog none;
type_name uint8_t EsNodeType;
type_name intptr_t EsError;
@ -2379,9 +2380,10 @@ function void EsMenuShow(EsMenu *menu, int fixedWidth = 0, int fixedHeight = 0);
function void EsMenuCloseAll();
function void EsMenuAddCommandsFromToolbar(EsMenu *menu, EsElement *element);
function void EsDialogClose(EsWindow *window);
function EsElement *EsDialogShow(EsWindow *window);
function EsElement *EsDialogShowAlert(EsWindow *window, STRING title, STRING content, uint32_t iconID, uint32_t flags = ES_FLAGS_DEFAULT); // Returns the button container.
function void EsDialogClose(EsDialog *dialog);
function EsDialog *EsDialogShow(EsWindow *window, STRING title, STRING content, uint32_t iconID, uint32_t flags = ES_FLAGS_DEFAULT);
function EsButton *EsDialogAddButton(EsDialog *dialog, uint64_t flags = ES_FLAGS_DEFAULT, EsStyle *style = ES_NULL,
STRING label = BLANK_STRING, EsCommandCallback callback = ES_NULL);
function void EsToolbarAddFileMenu(EsElement *element, const EsFileMenuSettings *settings = ES_NULL);

View File

@ -17,8 +17,7 @@ EsCommandRegister=15
EsCommandSetCallback=16
EsCommandSetDisabled=17
EsDialogClose=18
EsDialogShow=19
EsDialogShowAlert=20
EsDialogAddButton=19
EsCRTrand=21
EsCRTsnprintf=22
EsCRTsprintf=23
@ -461,3 +460,4 @@ EsBufferWriteInt8=459
EsInstanceGetStartupRequest=460
EsImageDisplayPaint=461
EsElementGetInsetBounds=462
EsDialogShow=463