mirror of https://gitlab.com/nakst/essence
show keyboard shortcuts in menus
This commit is contained in:
parent
2e4f92b619
commit
aabd7dbf36
134
desktop/gui.cpp
134
desktop/gui.cpp
|
@ -293,7 +293,6 @@ struct MeasurementCache {
|
|||
struct EsButton : EsElement {
|
||||
char *label;
|
||||
size_t labelBytes;
|
||||
EsGeneric menuItemContext;
|
||||
uint32_t iconID;
|
||||
MeasurementCache measurementCache;
|
||||
EsCommand *command;
|
||||
|
@ -302,6 +301,11 @@ struct EsButton : EsElement {
|
|||
EsImageDisplay *imageDisplay;
|
||||
};
|
||||
|
||||
struct MenuItem : EsButton {
|
||||
// This shares the EsButton structure so that it can be used with EsButtonOnCommand.
|
||||
EsGeneric menuItemContext;
|
||||
};
|
||||
|
||||
struct EsImageDisplay : EsElement {
|
||||
void *source;
|
||||
size_t sourceBytes;
|
||||
|
@ -3688,7 +3692,7 @@ EsDialog *EsDialogShow(EsWindow *window, const char *title, ptrdiff_t titleBytes
|
|||
return dialog;
|
||||
}
|
||||
|
||||
EsButton *EsDialogAddButton(EsDialog *dialog, uint64_t flags, EsStyle *style, const char *label, ptrdiff_t labelBytes, EsCommandCallback callback) {
|
||||
EsButton *EsDialogAddButton(EsDialog *dialog, uint64_t flags, const EsStyle *style, const char *label, ptrdiff_t labelBytes, EsCommandCallback callback) {
|
||||
EsButton *button = EsButtonCreate(dialog->buttonArea, flags, style, label, labelBytes);
|
||||
|
||||
if (button) {
|
||||
|
@ -4154,31 +4158,125 @@ void EsButtonSetCheck(EsButton *button, EsCheckState checkState, bool sendUpdate
|
|||
button->MaybeRefreshStyle();
|
||||
}
|
||||
|
||||
void EsMenuAddItem(EsMenu *menu, uint64_t flags, const char *label, ptrdiff_t labelBytes, EsMenuCallback callback, EsGeneric context) {
|
||||
EsButton *button = (EsButton *) EsButtonCreate(menu,
|
||||
ES_BUTTON_NOT_FOCUSABLE | ES_BUTTON_MENU_ITEM | ES_CELL_H_FILL | flags, 0,
|
||||
label, labelBytes != -1 ? labelBytes : EsCStringLength(label));
|
||||
if (!button) return;
|
||||
button->userData = (void *) callback;
|
||||
void MenuItemGetKeyboardShortcutString(EsCommand *command, EsBuffer *buffer) {
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
|
||||
button->messageUser = [] (EsElement *element, EsMessage *message) {
|
||||
if (message->type == ES_MSG_MOUSE_LEFT_CLICK) {
|
||||
EsMenuCallback callback = (EsMenuCallback) element->userData.p;
|
||||
if (callback) callback((EsMenu *) element->window, ((EsButton *) element)->menuItemContext);
|
||||
const char *input = command->cKeyboardShortcut;
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (input[0] == 'C' && input[1] == 't' && input[2] == 'r' && input[3] == 'l' && input[4] == '+') {
|
||||
EsBufferFormat(buffer, "%c", 0x2303);
|
||||
input += 5;
|
||||
} else if (input[0] == 'S' && input[1] == 'h' && input[2] == 'i' && input[3] == 'f' && input[4] == 't' && input[5] == '+') {
|
||||
EsBufferFormat(buffer, "%c", 0x21E7);
|
||||
input += 6;
|
||||
} else if (input[0] == 'A' && input[1] == 'l' && input[2] == 't' && input[3] == '+') {
|
||||
EsBufferFormat(buffer, "%c", 0x2325);
|
||||
input += 4;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (*input != 0 && *input != '|') {
|
||||
EsBufferWrite(buffer, input++, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int ProcessMenuItemMessage(EsElement *element, EsMessage *message) {
|
||||
MenuItem *button = (MenuItem *) element;
|
||||
|
||||
if (message->type == ES_MSG_PAINT) {
|
||||
// Draw the label.
|
||||
|
||||
EsDrawContent(message->painter, element,
|
||||
ES_RECT_2S(message->painter->width, message->painter->height),
|
||||
button->label, button->labelBytes, 0, ES_FLAGS_DEFAULT);
|
||||
|
||||
// Draw the keyboard shortcut.
|
||||
// TODO If activated by the keyboard, show access keys instead.
|
||||
|
||||
uint8_t _buffer[64];
|
||||
EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) };
|
||||
MenuItemGetKeyboardShortcutString(button->command, &buffer);
|
||||
|
||||
EsDrawContent(message->painter, element,
|
||||
ES_RECT_2S(message->painter->width, message->painter->height),
|
||||
(const char *) _buffer, buffer.position, 0, ES_DRAW_CONTENT_CHANGE_ALIGNMENT | ES_TEXT_H_RIGHT | ES_TEXT_V_CENTER);
|
||||
} else if (message->type == ES_MSG_GET_WIDTH) {
|
||||
uint8_t _buffer[64];
|
||||
EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) };
|
||||
MenuItemGetKeyboardShortcutString(button->command, &buffer);
|
||||
|
||||
EsTextStyle textStyle;
|
||||
button->currentStyle->GetTextStyle(&textStyle);
|
||||
|
||||
int stringWidth = TextGetStringWidth(button, &textStyle, button->label, button->labelBytes);
|
||||
int keyboardShortcutWidth = TextGetStringWidth(button, &textStyle, (const char *) _buffer, buffer.position);
|
||||
int contentWidth = stringWidth + button->currentStyle->insets.l + button->currentStyle->insets.r
|
||||
+ (keyboardShortcutWidth ? (keyboardShortcutWidth + button->currentStyle->gapMinor) : 0);
|
||||
message->measure.width = MaximumInteger(GetConstantNumber("menuItemMinimumReportedWidth"), contentWidth);
|
||||
} else if (message->type == ES_MSG_DESTROY) {
|
||||
EsHeapFree(button->label);
|
||||
|
||||
if (button->command) {
|
||||
Array<EsElement *> elements = { button->command->elements };
|
||||
elements.FindAndDeleteSwap(button, true);
|
||||
button->command->elements = elements.array;
|
||||
}
|
||||
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
|
||||
} else if (message->type == ES_MSG_MOUSE_LEFT_CLICK) {
|
||||
if (~element->flags & ES_ELEMENT_DISABLED) {
|
||||
EsMenuCallback callback = (EsMenuCallback) element->userData.p;
|
||||
|
||||
if (button->onCommand) {
|
||||
button->onCommand(button->instance, button, button->command);
|
||||
} else if (callback) {
|
||||
callback((EsMenu *) element->window, button->menuItemContext);
|
||||
}
|
||||
|
||||
EsAssert(button->window->windowStyle == ES_WINDOW_MENU);
|
||||
EsMenuClose((EsMenu *) button->window);
|
||||
}
|
||||
} else if (message->type == ES_MSG_GET_INSPECTOR_INFORMATION) {
|
||||
EsBufferFormat(message->getContent.buffer, "'%s'", button->labelBytes, button->label);
|
||||
} else {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
MenuItem *MenuItemCreate(EsMenu *menu, uint64_t flags, const char *label, ptrdiff_t labelBytes) {
|
||||
MenuItem *button = (MenuItem *) EsHeapAllocate(sizeof(MenuItem), true);
|
||||
if (!button) return nullptr;
|
||||
const EsStyle *style = (flags & ES_MENU_ITEM_HEADER) ? ES_STYLE_MENU_ITEM_HEADER : ES_STYLE_MENU_ITEM_NORMAL;
|
||||
if (flags & ES_MENU_ITEM_HEADER) flags |= ES_ELEMENT_DISABLED;
|
||||
button->Initialise(menu, flags | ES_CELL_H_FILL, ProcessMenuItemMessage, style);
|
||||
button->cName = "menu item";
|
||||
if (labelBytes == -1) labelBytes = EsCStringLength(label);
|
||||
HeapDuplicate((void **) &button->label, &button->labelBytes, label, labelBytes);
|
||||
return button;
|
||||
}
|
||||
|
||||
void EsMenuAddItem(EsMenu *menu, uint64_t flags, const char *label, ptrdiff_t labelBytes, EsMenuCallback callback, EsGeneric context) {
|
||||
MenuItem *button = MenuItemCreate(menu, flags, label, labelBytes);
|
||||
if (!button) return;
|
||||
EsButtonSetCheck(button, (EsCheckState) (flags & 3), false);
|
||||
button->MaybeRefreshStyle();
|
||||
button->userData = (void *) callback;
|
||||
button->menuItemContext = context;
|
||||
}
|
||||
|
||||
void EsMenuAddCommand(EsMenu *menu, uint64_t flags, const char *label, ptrdiff_t labelBytes, EsCommand *command) {
|
||||
EsButton *button = (EsButton *) EsButtonCreate(menu,
|
||||
ES_BUTTON_NOT_FOCUSABLE | ES_BUTTON_MENU_ITEM | ES_CELL_H_FILL | flags,
|
||||
0, label, labelBytes);
|
||||
if (!button) return;
|
||||
EsCommandAddButton(command, button);
|
||||
MenuItem *button = MenuItemCreate(menu, flags, label, labelBytes);
|
||||
if (button) EsCommandAddButton(command, button);
|
||||
}
|
||||
|
||||
// --------------------------------- Color wells and pickers.
|
||||
|
|
|
@ -1778,15 +1778,8 @@ struct EsListView : EsElement {
|
|||
|
||||
message->zOrder.child = nullptr;
|
||||
} else if (message->type == ES_MSG_PAINT && !totalItemCount && emptyMessageBytes) {
|
||||
UIStyle *style = GetStyle(MakeStyleKey(ES_STYLE_TEXT_LABEL_SECONDARY, 0), true);
|
||||
EsTextPlanProperties properties = {};
|
||||
properties.flags = ES_TEXT_H_CENTER | ES_TEXT_V_CENTER | ES_TEXT_WRAP | ES_TEXT_PLAN_SINGLE_USE;
|
||||
EsTextRun textRun[2] = {};
|
||||
style->GetTextStyle(&textRun[0].style);
|
||||
textRun[1].offset = emptyMessageBytes;
|
||||
EsRectangle bounds = EsPainterBoundsInset(message->painter);
|
||||
EsTextPlan *plan = EsTextPlanCreate(this, &properties, bounds, emptyMessage, textRun, 1);
|
||||
EsDrawText(message->painter, plan, bounds);
|
||||
EsDrawTextThemed(message->painter, this, EsPainterBoundsInset(message->painter), emptyMessage, emptyMessageBytes,
|
||||
ES_STYLE_TEXT_LABEL_SECONDARY, ES_TEXT_H_CENTER | ES_TEXT_V_CENTER | ES_TEXT_WRAP);
|
||||
} else if (message->type == ES_MSG_ANIMATE) {
|
||||
if (scroll.dragScrolling && (flags & ES_LIST_VIEW_CHOICE_SELECT)) {
|
||||
DragSelect();
|
||||
|
|
|
@ -362,19 +362,28 @@ define ES_LIST_VIEW_COLUMN_SORT_DESCENDING (2)
|
|||
define ES_SHARED_MEMORY_NAME_MAX_LENGTH (32)
|
||||
define ES_MAP_OBJECT_ALL (0)
|
||||
|
||||
define ES_TEXT_H_LEFT (1 << 0) // Keep in sync with designer.c.
|
||||
define ES_TEXT_H_CENTER (1 << 1)
|
||||
define ES_TEXT_H_RIGHT (1 << 2)
|
||||
define ES_TEXT_V_TOP (1 << 3)
|
||||
define ES_TEXT_V_CENTER (1 << 4)
|
||||
define ES_TEXT_V_BOTTOM (1 << 5)
|
||||
define ES_TEXT_ELLIPSIS (1 << 6)
|
||||
define ES_TEXT_WRAP (1 << 7)
|
||||
define ES_TEXT_PLAN_SINGLE_USE (1 << 8)
|
||||
define ES_TEXT_PLAN_TRIM_SPACES (1 << 9)
|
||||
define ES_TEXT_H_LEFT (1 << 0)
|
||||
define ES_TEXT_H_CENTER (1 << 1)
|
||||
define ES_TEXT_H_RIGHT (1 << 2)
|
||||
define ES_TEXT_V_TOP (1 << 3)
|
||||
define ES_TEXT_V_CENTER (1 << 4)
|
||||
define ES_TEXT_V_BOTTOM (1 << 5)
|
||||
define ES_TEXT_ELLIPSIS (1 << 6)
|
||||
define ES_TEXT_WRAP (1 << 7)
|
||||
|
||||
define ES_TEXT_PLAN_SINGLE_USE (1 << 8)
|
||||
define ES_TEXT_PLAN_TRIM_SPACES (1 << 9)
|
||||
define ES_TEXT_PLAN_RTL (1 << 10)
|
||||
define ES_TEXT_PLAN_CLIP_UNBREAKABLE_LINES (1 << 11)
|
||||
define ES_TEXT_PLAN_NO_FONT_SUBSTITUTION (1 << 12)
|
||||
// ...plus alignment flags.
|
||||
|
||||
define ES_DRAW_CONTENT_CHANGE_ALIGNMENT (1 << 8)
|
||||
define ES_DRAW_CONTENT_MARKER_DOWN_ARROW (1 << 9)
|
||||
define ES_DRAW_CONTENT_MARKER_UP_ARROW (1 << 10)
|
||||
define ES_DRAW_CONTENT_TABULAR (1 << 11)
|
||||
define ES_DRAW_CONTENT_RICH_TEXT (1 << 12)
|
||||
// ...plus alignment flags, if CHANGE_ALIGNMENT set.
|
||||
|
||||
define ES_FILE_READ_SHARED (0x1) // Read-only. The file can still be opened for writing.
|
||||
define ES_FILE_READ (0x2) // Read-only. The file will not openable for writing. This will fail if the file is already opened for writing.
|
||||
|
@ -693,11 +702,6 @@ define ES_APPLICATION_STARTUP_BACKGROUND_SERVICE (1 << 1)
|
|||
define ES_LIST_VIEW_INLINE_TEXTBOX_COPY_EXISTING_TEXT (1 << 0)
|
||||
define ES_LIST_VIEW_INLINE_TEXTBOX_REJECT_EDIT_IF_FOCUS_LOST (1 << 1)
|
||||
|
||||
define ES_DRAW_CONTENT_MARKER_DOWN_ARROW (1 << 0)
|
||||
define ES_DRAW_CONTENT_MARKER_UP_ARROW (1 << 1)
|
||||
define ES_DRAW_CONTENT_TABULAR (1 << 2)
|
||||
define ES_DRAW_CONTENT_RICH_TEXT (1 << 3)
|
||||
|
||||
define ES_MODIFIER_CTRL (1 << 0)
|
||||
define ES_MODIFIER_SHIFT (1 << 1)
|
||||
define ES_MODIFIER_ALT (1 << 2)
|
||||
|
@ -2112,6 +2116,7 @@ function bool EsDrawStandardIcon(EsPainter *painter, uint32_t id, int size, EsRe
|
|||
function void EsDrawPaintTarget(EsPainter *painter, EsPaintTarget *source, EsRectangle destinationRegion, EsRectangle sourceRegion, uint8_t alpha);
|
||||
function void EsDrawText(EsPainter *painter, EsTextPlan *plan, EsRectangle bounds, EsRectangle *clip = ES_NULL, EsTextSelection *selectionProperties = ES_NULL);
|
||||
function void EsDrawTextSimple(EsPainter *painter, EsElement *element, EsRectangle bounds, const char *string, ptrdiff_t stringBytes, EsTextStyle style, uint32_t flags = ES_FLAGS_DEFAULT);
|
||||
function void EsDrawTextThemed(EsPainter *painter, EsElement *element, EsRectangle bounds, const char *string, ptrdiff_t stringBytes, const EsStyle *style, uint32_t flags = ES_FLAGS_DEFAULT); // The style must be one of the ES_STYLE_TEXT_... styles.
|
||||
function void EsDrawTextLayers(EsPainter *painter, EsTextPlan *plan, EsRectangle bounds, EsTextSelection *selectionProperties = ES_NULL);
|
||||
function void EsDrawVectorFile(EsPainter *painter, EsRectangle bounds, const void *data, size_t dataBytes);
|
||||
|
||||
|
@ -2398,7 +2403,7 @@ function void EsMenuAddCommandsFromToolbar(EsMenu *menu, EsElement *element);
|
|||
|
||||
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,
|
||||
function EsButton *EsDialogAddButton(EsDialog *dialog, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL,
|
||||
STRING label = BLANK_STRING, EsCommandCallback callback = ES_NULL);
|
||||
|
||||
function void EsFileMenuAddToToolbar(EsElement *toolbar, const EsFileMenuSettings *settings = ES_NULL);
|
||||
|
|
|
@ -2594,9 +2594,16 @@ void EsDrawTextSimple(EsPainter *painter, EsElement *element, EsRectangle bounds
|
|||
EsTextRun textRuns[2] = {};
|
||||
textRuns[0].style = style;
|
||||
textRuns[1].offset = stringBytes == -1 ? EsCStringLength(string) : stringBytes;
|
||||
if (!textRuns[1].offset) return;
|
||||
EsDrawText(painter, EsTextPlanCreate(element, &properties, bounds, string, textRuns, 1), bounds);
|
||||
}
|
||||
|
||||
void EsDrawTextThemed(EsPainter *painter, EsElement *element, EsRectangle bounds, const char *string, ptrdiff_t stringBytes, const EsStyle *style, uint32_t flags) {
|
||||
EsTextStyle textStyle;
|
||||
GetStyle(MakeStyleKey(style, 0), true)->GetTextStyle(&textStyle);
|
||||
EsDrawTextSimple(painter, element, bounds, string, stringBytes, textStyle, flags);
|
||||
}
|
||||
|
||||
#elif defined(TEXT_ELEMENTS)
|
||||
|
||||
// --------------------------------- Markup parsing.
|
||||
|
|
|
@ -1900,7 +1900,7 @@ void UIStyle::PaintText(EsPainter *painter, EsElement *element, EsRectangle rect
|
|||
|
||||
if (textBytes) {
|
||||
EsTextPlanProperties properties = {};
|
||||
properties.flags = textAlign;
|
||||
properties.flags = (flags & ES_DRAW_CONTENT_CHANGE_ALIGNMENT) ? (flags & 0xFF) : textAlign;
|
||||
|
||||
EsTextRun textRun[2] = {};
|
||||
textRun[1].offset = textBytes;
|
||||
|
|
Binary file not shown.
BIN
res/Theme.dat
BIN
res/Theme.dat
Binary file not shown.
|
@ -476,3 +476,4 @@ EsCRTatof=474
|
|||
EsCRTstrtod=475
|
||||
EsCRTstrtof=476
|
||||
EsOpenDocumentQueryInformation=477
|
||||
EsDrawTextThemed=478
|
||||
|
|
Loading…
Reference in New Issue