From 48bb528b9dbe784e92a966b2ce9c5ad340491fc6 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Sun, 21 Nov 2021 12:27:50 +0000 Subject: [PATCH] fixed item column formatters --- apps/font_book.cpp | 36 ++++----- apps/samples/list.cpp | 37 ++++++--- apps/text_editor.cpp | 12 +-- desktop/list_view.cpp | 179 ++++++++++++++++++++++++++++++++++-------- desktop/os.header | 42 ++++++++-- desktop/settings.cpp | 2 +- shared/common.cpp | 26 ++++-- shared/strings.cpp | 17 +++- util/api_table.ini | 5 +- 9 files changed, 273 insertions(+), 83 deletions(-) diff --git a/apps/font_book.cpp b/apps/font_book.cpp index 050ecbb..b0f15bf 100644 --- a/apps/font_book.cpp +++ b/apps/font_book.cpp @@ -238,24 +238,24 @@ void VariantsPopupCreate(Instance *instance, EsElement *element, EsCommand *) { EsPanel *panel = EsPanelCreate(menu, ES_PANEL_HORIZONTAL, ES_STYLE_PANEL_POPUP); EsListView *list = EsListViewCreate(panel, ES_LIST_VIEW_CHOICE_SELECT | ES_LIST_VIEW_FIXED_ITEMS, ES_STYLE_LIST_CHOICE_BORDERED); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 1), 0, INTERFACE_STRING(FontBookVariantNormal100)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 2), 0, INTERFACE_STRING(FontBookVariantNormal200)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 3), 0, INTERFACE_STRING(FontBookVariantNormal300)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 4), 0, INTERFACE_STRING(FontBookVariantNormal400)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 5), 0, INTERFACE_STRING(FontBookVariantNormal500)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 6), 0, INTERFACE_STRING(FontBookVariantNormal600)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 7), 0, INTERFACE_STRING(FontBookVariantNormal700)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 8), 0, INTERFACE_STRING(FontBookVariantNormal800)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 9), 0, INTERFACE_STRING(FontBookVariantNormal900)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 11), 0, INTERFACE_STRING(FontBookVariantItalic100)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 12), 0, INTERFACE_STRING(FontBookVariantItalic200)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 13), 0, INTERFACE_STRING(FontBookVariantItalic300)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 14), 0, INTERFACE_STRING(FontBookVariantItalic400)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 15), 0, INTERFACE_STRING(FontBookVariantItalic500)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 16), 0, INTERFACE_STRING(FontBookVariantItalic600)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 17), 0, INTERFACE_STRING(FontBookVariantItalic700)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 18), 0, INTERFACE_STRING(FontBookVariantItalic800)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 19), 0, INTERFACE_STRING(FontBookVariantItalic900)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 1), 0, INTERFACE_STRING(FontBookVariantNormal100)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 2), 0, INTERFACE_STRING(FontBookVariantNormal200)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 3), 0, INTERFACE_STRING(FontBookVariantNormal300)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 4), 0, INTERFACE_STRING(FontBookVariantNormal400)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 5), 0, INTERFACE_STRING(FontBookVariantNormal500)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 6), 0, INTERFACE_STRING(FontBookVariantNormal600)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 7), 0, INTERFACE_STRING(FontBookVariantNormal700)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 8), 0, INTERFACE_STRING(FontBookVariantNormal800)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 9), 0, INTERFACE_STRING(FontBookVariantNormal900)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 11), 0, INTERFACE_STRING(FontBookVariantItalic100)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 12), 0, INTERFACE_STRING(FontBookVariantItalic200)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 13), 0, INTERFACE_STRING(FontBookVariantItalic300)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 14), 0, INTERFACE_STRING(FontBookVariantItalic400)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 15), 0, INTERFACE_STRING(FontBookVariantItalic500)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 16), 0, INTERFACE_STRING(FontBookVariantItalic600)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 17), 0, INTERFACE_STRING(FontBookVariantItalic700)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 18), 0, INTERFACE_STRING(FontBookVariantItalic800)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 19), 0, INTERFACE_STRING(FontBookVariantItalic900)); EsListViewFixedItemSelect(list, instance->fontVariant); list->messageUser = [] (EsElement *element, EsMessage *message) { diff --git a/apps/samples/list.cpp b/apps/samples/list.cpp index 9966034..040986e 100644 --- a/apps/samples/list.cpp +++ b/apps/samples/list.cpp @@ -4,14 +4,20 @@ #define COLUMN_AGE (1) #define COLUMN_FAVORITE_COLOR (2) -void AddPerson(EsListView *list, const char *name, int age, const char *favoriteColor) { - char ageString[16]; - EsStringFormat(ageString, sizeof(ageString), "%d%c", age, 0); +const EsListViewEnumString colorStrings[] = { +#define COLOR_RED (0) + { "\a#e00]Red", -1 }, +#define COLOR_GREEN (1) + { "\a#080]Green", -1 }, +#define COLOR_BLUE (2) + { "\a#00f]Blue", -1 }, +}; +void AddPerson(EsListView *list, const char *name, int age, int favoriteColor) { EsListViewIndex index = EsListViewFixedItemInsert(list); - EsListViewFixedItemSet(list, index, COLUMN_NAME, name); - EsListViewFixedItemSet(list, index, COLUMN_AGE, ageString); - EsListViewFixedItemSet(list, index, COLUMN_FAVORITE_COLOR, favoriteColor); + EsListViewFixedItemSetString (list, index, COLUMN_NAME, name); + EsListViewFixedItemSetInteger(list, index, COLUMN_AGE, age); + EsListViewFixedItemSetInteger(list, index, COLUMN_FAVORITE_COLOR, favoriteColor); } void _start() { @@ -23,14 +29,21 @@ void _start() { if (message->type == ES_MSG_INSTANCE_CREATE) { EsInstance *instance = EsInstanceCreate(message, "List", -1); EsPanel *wrapper = EsPanelCreate(instance->window, ES_CELL_FILL, ES_STYLE_PANEL_WINDOW_DIVIDER); + EsListView *list = EsListViewCreate(wrapper, ES_CELL_FILL | ES_LIST_VIEW_COLUMNS | ES_LIST_VIEW_FIXED_ITEMS); - EsListViewRegisterColumn(list, COLUMN_NAME, "Name", -1, ES_FLAGS_DEFAULT, 150); - EsListViewRegisterColumn(list, COLUMN_AGE, "Age", -1, ES_TEXT_H_RIGHT, 100); - EsListViewRegisterColumn(list, COLUMN_FAVORITE_COLOR, "Favorite color", -1, ES_DRAW_CONTENT_RICH_TEXT, 150); + EsListViewRegisterColumn(list, COLUMN_NAME, "Name", -1, + ES_FLAGS_DEFAULT, 150); + EsListViewRegisterColumn(list, COLUMN_AGE, "Age", -1, + ES_TEXT_H_RIGHT | ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS, 100); + EsListViewRegisterColumn(list, COLUMN_FAVORITE_COLOR, "Favorite color", -1, + ES_DRAW_CONTENT_RICH_TEXT | ES_LIST_VIEW_COLUMN_FIXED_FORMAT_ENUM_STRING | ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS, 150); + EsListViewFixedItemSetEnumStringsForColumn(list, COLUMN_FAVORITE_COLOR, colorStrings, sizeof(colorStrings) / sizeof(colorStrings[0])); EsListViewAddAllColumns(list); - AddPerson(list, "Alice", 20, "\a#e00]Red"); - AddPerson(list, "Bob", 30, "\a#080]Green"); - AddPerson(list, "Cameron", 40, "\a#00f]Blue"); + + AddPerson(list, "Alice", 10, COLOR_RED); + AddPerson(list, "Bob", 20, COLOR_GREEN); + AddPerson(list, "Cameron", 30, COLOR_BLUE); + AddPerson(list, "Daniel", 40, COLOR_RED); } } } diff --git a/apps/text_editor.cpp b/apps/text_editor.cpp index 588b984..53bdde9 100644 --- a/apps/text_editor.cpp +++ b/apps/text_editor.cpp @@ -141,17 +141,17 @@ void FormatPopupCreate(Instance *instance) { if (currentSize < presetTextSizes[0]) { // The current size is not in the list; add it. - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, currentSize), 0, buffer, + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, currentSize), 0, buffer, EsStringFormat(buffer, sizeof(buffer), "%d pt", currentSize)); } for (uintptr_t i = 0; i < presetSizeCount; i++) { - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, presetTextSizes[i]), 0, buffer, + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, presetTextSizes[i]), 0, buffer, EsStringFormat(buffer, sizeof(buffer), "%d pt", presetTextSizes[i])); if (currentSize > presetTextSizes[i] && (i == presetSizeCount - 1 || (i != presetSizeCount - 1 && currentSize < presetTextSizes[i + 1]))) { // The current size is not in the list; add it. - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, currentSize), 0, buffer, + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, currentSize), 0, buffer, EsStringFormat(buffer, sizeof(buffer), "%d pt", currentSize)); } } @@ -177,9 +177,9 @@ void FormatPopupCreate(Instance *instance) { EsPanel *column = EsPanelCreate(panel, ES_FLAGS_DEFAULT, &styleFormatPopupColumn); EsTextDisplayCreate(column, ES_CELL_H_EXPAND, ES_STYLE_TEXT_LABEL, INTERFACE_STRING(CommonFormatLanguage)); EsListView *list = EsListViewCreate(column, ES_LIST_VIEW_CHOICE_SELECT | ES_LIST_VIEW_FIXED_ITEMS, ES_STYLE_LIST_CHOICE_BORDERED); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, 0), 0, INTERFACE_STRING(CommonFormatPlainText)); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C), 0, "C/C++", -1); - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_INI), 0, "Ini", -1); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, 0), 0, INTERFACE_STRING(CommonFormatPlainText)); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C), 0, "C/C++", -1); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_INI), 0, "Ini", -1); EsListViewFixedItemSelect(list, instance->syntaxHighlightingLanguage); list->messageUser = [] (EsElement *element, EsMessage *message) { diff --git a/desktop/list_view.cpp b/desktop/list_view.cpp index 5c0b7db..8866ead 100644 --- a/desktop/list_view.cpp +++ b/desktop/list_view.cpp @@ -42,13 +42,23 @@ struct ListViewFixedItem { uint32_t iconID; }; +struct ListViewFixedItemData { + union { + ListViewFixedString s; + double d; + int64_t i; + }; +}; + struct ListViewColumn { char *title; size_t titleBytes; uint32_t id; uint32_t flags; double width; - Array items; + Array items; + const EsListViewEnumString *enumStrings; + size_t enumStringCount; }; int ListViewProcessItemMessage(EsElement *element, EsMessage *message); @@ -1678,8 +1688,10 @@ struct EsListView : EsElement { } for (uintptr_t i = 0; i < registeredColumns.Length(); i++) { - for (uintptr_t j = 0; j < registeredColumns[i].items.Length(); j++) { - EsHeapFree(registeredColumns[i].items[j].string); + if ((registeredColumns[i].flags & ES_LIST_VIEW_COLUMN_FIXED_DATA_MASK) == ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS) { + for (uintptr_t j = 0; j < registeredColumns[i].items.Length(); j++) { + EsHeapFree(registeredColumns[i].items[j].s.string); + } } EsHeapFree(registeredColumns[i].title); @@ -1882,12 +1894,82 @@ struct EsListView : EsElement { } else if (message->type == ES_MSG_LIST_VIEW_GET_CONTENT && (flags & ES_LIST_VIEW_FIXED_ITEMS)) { uintptr_t index = message->getContent.index; EsAssert(index < fixedItems.Length()); - ListViewFixedString emptyString = {}; + ListViewFixedItemData emptyData = {}; ListViewFixedItem *item = &fixedItems[index]; ListViewColumn *column = ®isteredColumns[(flags & ES_LIST_VIEW_COLUMNS) ? activeColumns[message->getContent.activeColumnIndex] : 0]; - ListViewFixedString *string = index < column->items.Length() ? &column->items[index] : &emptyString; - EsBufferFormat(message->getContent.buffer, "%s", string->bytes, string->string); - if (!activeColumns.Length() || message->getContent.columnID == registeredColumns[activeColumns[0]].id) message->getContent.icon = item->iconID; + ListViewFixedItemData *data = index < column->items.Length() ? &column->items[index] : &emptyData; + uint32_t format = column->flags & ES_LIST_VIEW_COLUMN_FIXED_FORMAT_MASK; + uint32_t type = column->flags & ES_LIST_VIEW_COLUMN_FIXED_DATA_MASK; + + if (!activeColumns.Length() || message->getContent.columnID == registeredColumns[activeColumns[0]].id) { + message->getContent.icon = item->iconID; + } + +#define BOOLEAN_FORMAT(trueString, falseString) \ + if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS) { \ + EsBufferFormat(message->getContent.buffer, "%z", data->i ? interfaceString_ ## trueString : interfaceString_ ## falseString); \ + } else { \ + EsAssert(false); \ + } +#define NUMBER_FORMAT(unitString) \ + if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS) { \ + EsBufferFormat(message->getContent.buffer, "%d%z", data->i, interfaceString_ ## unitString); \ + } else if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_DOUBLES) { \ + EsBufferFormat(message->getContent.buffer, "%F%z", data->d, interfaceString_ ## unitString); \ + } else { \ + EsAssert(false); \ + } +#define UNIT_FORMAT(unitString1, unitString2, unitString3) \ + double d = type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS ? data->i : type == ES_LIST_VIEW_COLUMN_FIXED_DATA_DOUBLES ? data->d : 0; \ + if (d < 10000) EsBufferFormat(message->getContent.buffer, "%F%z", d, interfaceString_ ## unitString1); \ + else if (d < 10000000) EsBufferFormat(message->getContent.buffer, "%.F%z", 1, d / 1000, interfaceString_ ## unitString2); \ + else EsBufferFormat(message->getContent.buffer, "%.F%z", 1, d / 1000000, interfaceString_ ## unitString3); + + if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_DEFAULT) { + if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS) { + EsBufferFormat(message->getContent.buffer, "%s", data->s.bytes, data->s.string); + } else if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_DOUBLES) { + EsBufferFormat(message->getContent.buffer, "%F", data->d); + } else if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS) { + EsBufferFormat(message->getContent.buffer, "%d", data->i); + } + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BYTES) { + if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS) { + EsBufferFormat(message->getContent.buffer, "%D", data->i); + } else { + EsAssert(false); + } + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_ENUM_STRING) { + if (type == ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS) { + EsAssert(data->i >= 0 && (uintptr_t) data->i < column->enumStringCount); + EsBufferFormat(message->getContent.buffer, "%s", column->enumStrings[data->i].stringBytes, column->enumStrings[data->i].string); + } else { + EsAssert(false); + } + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_YES_NO) { + BOOLEAN_FORMAT(CommonBooleanYes, CommonBooleanNo); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_ON_OFF) { + BOOLEAN_FORMAT(CommonBooleanOn, CommonBooleanOff); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_PERCENTAGE) { + NUMBER_FORMAT(CommonUnitPercent); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BITS) { + NUMBER_FORMAT(CommonUnitBits); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_PIXELS) { + NUMBER_FORMAT(CommonUnitPixels); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_DPI) { + NUMBER_FORMAT(CommonUnitDPI); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_SECONDS) { + NUMBER_FORMAT(CommonUnitSeconds); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_HERTZ) { + UNIT_FORMAT(CommonUnitHz, CommonUnitKHz, CommonUnitMHz); + } else if (format == ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BYTE_RATE) { + UNIT_FORMAT(CommonUnitBps, CommonUnitKBps, CommonUnitMBps); + } else { + EsAssert(false); + } + +#undef NUMBER_FORMAT +#undef BOOLEAN_FORMAT } else if (message->type == ES_MSG_LIST_VIEW_IS_SELECTED && (flags & ES_LIST_VIEW_FIXED_ITEMS)) { message->selectItem.isSelected = message->selectItem.index == fixedItemSelection; } else { @@ -2452,13 +2534,13 @@ EsListViewIndex EsListViewFixedItemInsert(EsListView *view, EsGeneric data, EsLi item.iconID = iconID; view->fixedItems.Insert(item, index); - ListViewFixedString emptyString = {}; + ListViewFixedItemData emptyData = {}; for (uintptr_t i = 0; i < view->registeredColumns.Length(); i++) { ListViewColumn *column = &view->registeredColumns[i]; if (column->items.Length() >= (uintptr_t) index) { - column->items.InsertPointer(&emptyString, index); + column->items.InsertPointer(&emptyData, index); } } @@ -2467,40 +2549,57 @@ EsListViewIndex EsListViewFixedItemInsert(EsListView *view, EsGeneric data, EsLi return index; } -void EsListViewFixedItemSet(EsListView *view, EsListViewIndex index, uint32_t columnID, const char *string, ptrdiff_t stringBytes) { +void ListViewFixedItemSetInternal(EsListView *view, EsListViewIndex index, uint32_t columnID, ListViewFixedItemData data, uint32_t dataType) { EsAssert(view->flags & ES_LIST_VIEW_FIXED_ITEMS); EsMessageMutexCheck(); EsAssert(index >= 0 && index < (intptr_t) view->fixedItems.Length()); + + ListViewColumn *column = nullptr; + + for (uintptr_t i = 0; i < view->registeredColumns.Length(); i++) { + if (view->registeredColumns[i].id == columnID) { + column = &view->registeredColumns[i]; + break; + } + } + + EsAssert(column); + EsAssert((column->flags & ES_LIST_VIEW_COLUMN_FIXED_DATA_MASK) == dataType); + + // Make sure that the column's array of items has been updated to match to the size of fixedItems. + if (column->items.Length() < view->fixedItems.Length()) { + uintptr_t oldLength = column->items.Length(); + column->items.SetLength(view->fixedItems.Length()); + EsMemoryZero(&column->items[oldLength], (view->fixedItems.Length() - oldLength) * sizeof(column->items[0])); + } + + if (dataType == ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS) { + EsHeapFree(column->items[index].s.string); + } + + column->items[index] = data; + EsListViewInvalidateContent(view, 0, index); +} + +void EsListViewFixedItemSetString(EsListView *view, EsListViewIndex index, uint32_t columnID, const char *string, ptrdiff_t stringBytes) { ListViewFixedString fixedString = {}; fixedString.bytes = stringBytes == -1 ? EsCStringLength(string) : stringBytes; size_t outBytes; HeapDuplicate((void **) &fixedString, &outBytes, string, fixedString.bytes); if (outBytes == fixedString.bytes) { - ListViewColumn *column = nullptr; - - for (uintptr_t i = 0; i < view->registeredColumns.Length(); i++) { - if (view->registeredColumns[i].id == columnID) { - column = &view->registeredColumns[i]; - break; - } - } - - EsAssert(column); - - // Make sure that the column's array of items has been updated to match to the size of fixedItems. - if (column->items.Length() < view->fixedItems.Length()) { - uintptr_t oldLength = column->items.Length(); - column->items.SetLength(view->fixedItems.Length()); - EsMemoryZero(&column->items[oldLength], (view->fixedItems.Length() - oldLength) * sizeof(column->items[0])); - } - - EsHeapFree(column->items[index].string); - column->items[index] = fixedString; - EsListViewInvalidateContent(view, 0, index); + ListViewFixedItemSetInternal(view, index, columnID, { .s = fixedString }, ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS); } } +void EsListViewFixedItemSetDouble(EsListView *view, EsListViewIndex index, uint32_t columnID, double number) { + ListViewFixedItemSetInternal(view, index, columnID, { .d = number }, ES_LIST_VIEW_COLUMN_FIXED_DATA_DOUBLES); +} + +void EsListViewFixedItemSetInteger(EsListView *view, EsListViewIndex index, uint32_t columnID, int64_t number) { + ListViewFixedItemSetInternal(view, index, columnID, { .i = number }, ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS); +} + bool EsListViewFixedItemFindIndex(EsListView *view, EsGeneric data, EsListViewIndex *index) { EsAssert(view->flags & ES_LIST_VIEW_FIXED_ITEMS); EsMessageMutexCheck(); @@ -2545,7 +2644,10 @@ bool EsListViewFixedItemRemove(EsListView *view, EsGeneric data) { ListViewColumn *column = &view->registeredColumns[i]; if ((uintptr_t) index < column->items.Length()) { - EsHeapFree(column->items[index].string); + if ((column->flags & ES_LIST_VIEW_COLUMN_FIXED_DATA_MASK) == ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS) { + EsHeapFree(column->items[index].s.string); + } + column->items.Delete(index); if (!column->items.Length()) { @@ -2572,6 +2674,19 @@ bool EsListViewFixedItemGetSelected(EsListView *view, EsGeneric *data) { } } +void EsListViewFixedItemSetEnumStringsForColumn(EsListView *view, uint32_t columnID, const EsListViewEnumString *strings, size_t stringCount) { + for (uintptr_t i = 0; i < view->registeredColumns.Length(); i++) { + if (view->registeredColumns[i].id == columnID) { + view->registeredColumns[i].enumStrings = strings; + view->registeredColumns[i].enumStringCount = stringCount; + EsListViewInvalidateAll(view); + return; + } + } + + EsAssert(false); +} + int ListViewInlineTextboxMessage(EsElement *element, EsMessage *message) { int response = ProcessTextboxMessage(element, message); diff --git a/desktop/os.header b/desktop/os.header index 7a2f120..d66049f 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -357,9 +357,6 @@ define ES_SYSTEM_SNAPSHOT_PROCESSES (1) define ES_HANDLED (-1) define ES_REJECTED (-2) -define ES_LIST_VIEW_COLUMN_SORT_ASCENDING (1) -define ES_LIST_VIEW_COLUMN_SORT_DESCENDING (2) - define ES_TEXT_H_LEFT (1 << 0) define ES_TEXT_H_CENTER (1 << 1) define ES_TEXT_H_RIGHT (1 << 2) @@ -380,11 +377,37 @@ define ES_DRAW_CONTENT_TABULAR (1 << 8) define ES_DRAW_CONTENT_MARKER_DOWN_ARROW (1 << 9) define ES_DRAW_CONTENT_MARKER_UP_ARROW (1 << 10) define ES_DRAW_CONTENT_RICH_TEXT (1 << 12) -// ...plus alignment flags, if CHANGE_ALIGNMENT set. +// ...plus alignment flags. -define ES_LIST_VIEW_COLUMN_HAS_MENU (1 << 16) // The header can be clicked to open a menu. +define ES_LIST_VIEW_COLUMN_HAS_MENU ( 1 << 16) // The header can be clicked to open a menu. +define ES_LIST_VIEW_COLUMN_FIXED_DATA_STRINGS ( 0 << 22) // Data types for fixed items. +define ES_LIST_VIEW_COLUMN_FIXED_DATA_INTEGERS ( 1 << 22) +define ES_LIST_VIEW_COLUMN_FIXED_DATA_DOUBLES ( 2 << 22) +define ES_LIST_VIEW_COLUMN_FIXED_DATA_MASK ( 3 << 22) +define ES_LIST_VIEW_COLUMN_FIXED_SORT_DEFAULT ( 0 << 24) // Sort types for fixed items. +define ES_LIST_VIEW_COLUMN_FIXED_SORT_TIME ( 1 << 24) +define ES_LIST_VIEW_COLUMN_FIXED_SORT_SIZE ( 2 << 24) +define ES_LIST_VIEW_COLUMN_FIXED_SORT_NONE ( 3 << 24) +define ES_LIST_VIEW_COLUMN_FIXED_SORT_MASK ( 7 << 24) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_DEFAULT ( 0 << 27) // Format types for fixed items. +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_YES_NO ( 1 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_ON_OFF ( 2 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_PERCENTAGE ( 3 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BYTES ( 4 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BITS ( 5 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_BYTE_RATE ( 6 << 27) // Bytes per second. +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_SECONDS ( 7 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_HERTZ ( 8 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_PIXELS ( 9 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_DPI (10 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_ENUM_STRING (11 << 27) +define ES_LIST_VIEW_COLUMN_FIXED_FORMAT_MASK (31 << 27) // ...plus draw content flags and alignment flags. +// For ES_MSG_LIST_VIEW_GET_COLUMN_SORT. +define ES_LIST_VIEW_COLUMN_SORT_ASCENDING (1 << 0) +define ES_LIST_VIEW_COLUMN_SORT_DESCENDING (1 << 1) + 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. define ES_FILE_WRITE_SHARED (0x4) // Read-write. The file can still be opened for writing. This will fail if the file is already opened for exclusive writing. @@ -1944,6 +1967,10 @@ struct EsDateComponents { uint16_t millisecond; // 0-999. }; +struct EsListViewEnumString { + STRING string; +}; + // Function pointer types. function_pointer void EsThreadEntryCallback(EsGeneric argument); @@ -2591,7 +2618,10 @@ function void EsListViewRemoveAll(EsListView *view, EsListViewIndex group); // (Fixed items.) function EsListViewIndex EsListViewFixedItemInsert(EsListView *view, EsGeneric data = ES_NULL, EsListViewIndex index = -1, uint32_t iconID = 0); function bool EsListViewFixedItemRemove(EsListView *view, EsGeneric data); // Returns false if the item was not found. -function void EsListViewFixedItemSet(EsListView *view, EsListViewIndex index, uint32_t columnID, STRING string = BLANK_STRING); +function void EsListViewFixedItemSetString(EsListView *view, EsListViewIndex index, uint32_t columnID, STRING string = BLANK_STRING); +function void EsListViewFixedItemSetDouble(EsListView *view, EsListViewIndex index, uint32_t columnID, double number); +function void EsListViewFixedItemSetInteger(EsListView *view, EsListViewIndex index, uint32_t columnID, int64_t number); function bool EsListViewFixedItemFindIndex(EsListView *view, EsGeneric data, EsListViewIndex *index); // Returns false if the item was not found. function bool EsListViewFixedItemSelect(EsListView *view, EsGeneric data); // Returns false if the item was not found. function bool EsListViewFixedItemGetSelected(EsListView *view, EsGeneric *data); // Returns false if no item was selected. +function void EsListViewFixedItemSetEnumStringsForColumn(EsListView *view, uint32_t columnID, const EsListViewEnumString *strings, size_t stringCount); diff --git a/desktop/settings.cpp b/desktop/settings.cpp index 785f49e..7a036ce 100644 --- a/desktop/settings.cpp +++ b/desktop/settings.cpp @@ -649,7 +649,7 @@ void SettingsPageKeyboard(EsElement *element, SettingsPage *page) { while (EsINIParse(&s)) { if (s.key[0] != ';') { - EsListViewFixedItemSet(list, EsListViewFixedItemInsert(list, s.key), 0, s.value, s.valueBytes); + EsListViewFixedItemSetString(list, EsListViewFixedItemInsert(list, s.key), 0, s.value, s.valueBytes); EsAssert(s.keyBytes == 2); if (s.key[0] + ((uint16_t) s.key[1] << 8) == api.global->keyboardLayout) { diff --git a/shared/common.cpp b/shared/common.cpp index 72b7013..763f441 100644 --- a/shared/common.cpp +++ b/shared/common.cpp @@ -540,6 +540,7 @@ void WriteCStringToCallback(FormatCallback callback, void *callbackData, const c void _StringFormat(FormatCallback callback, void *callbackData, const char *format, va_list arguments) { int c; int pad = 0; + int decimalPlaces = -1; uint32_t flags = 0; char buffer[32]; @@ -647,13 +648,18 @@ void _StringFormat(FormatCallback callback, void *callbackData, const char *form } break; case 's': { - size_t length = va_arg(arguments, size_t); + ptrdiff_t length = va_arg(arguments, ptrdiff_t); char *string = va_arg(arguments, char *); - char *position = string; - while (position < string + length) { - callback(utf8_value(position), callbackData); - position = utf8_advance(position); + if (length == -1) { + WriteCStringToCallback(callback, callbackData, string ?: "[null]"); + } else { + char *position = string; + + while (position < string + length) { + callback(utf8_value(position), callbackData); + position = utf8_advance(position); + } } } break; @@ -731,14 +737,17 @@ void _StringFormat(FormatCallback callback, void *callbackData, const char *form _FormatInteger(callback, callbackData, integer, pad, flags & ES_STRING_FORMAT_SIMPLE); // Decimal separator. - if (digitCount) { + if (digitCount && decimalPlaces) { callback('.', callbackData); } // Fractional digits. for (uintptr_t i = 0; i < digitCount; i++) { + if ((int) i == decimalPlaces) break; callback('0' + digits[i], callbackData); } + + decimalPlaces = -1; } break; case '*': { @@ -746,6 +755,11 @@ void _StringFormat(FormatCallback callback, void *callbackData, const char *form goto repeat; } break; + case '.': { + decimalPlaces = va_arg(arguments, int); + goto repeat; + } break; + case 'f': { flags = va_arg(arguments, uint32_t); goto repeat; diff --git a/shared/strings.cpp b/shared/strings.cpp index f05f723..5146047 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -82,10 +82,25 @@ DEFINE_INTERFACE_STRING(CommonEmpty, "empty"); DEFINE_INTERFACE_STRING(CommonUnitPercent, "%"); DEFINE_INTERFACE_STRING(CommonUnitBytes, " B"); -DEFINE_INTERFACE_STRING(CommonUnitKilobytes, " KB"); +DEFINE_INTERFACE_STRING(CommonUnitKilobytes, " kB"); // We use this for 10^3, so let's go for the proper SI prefix, rather than the traditional "KB". DEFINE_INTERFACE_STRING(CommonUnitMegabytes, " MB"); DEFINE_INTERFACE_STRING(CommonUnitGigabytes, " GB"); DEFINE_INTERFACE_STRING(CommonUnitMilliseconds, " ms"); +DEFINE_INTERFACE_STRING(CommonUnitSeconds, " s"); +DEFINE_INTERFACE_STRING(CommonUnitBits, " bits"); +DEFINE_INTERFACE_STRING(CommonUnitPixels, " px"); +DEFINE_INTERFACE_STRING(CommonUnitDPI, " dpi"); +DEFINE_INTERFACE_STRING(CommonUnitBps, " Bps"); +DEFINE_INTERFACE_STRING(CommonUnitKBps, " kBps"); +DEFINE_INTERFACE_STRING(CommonUnitMBps, " MBps"); +DEFINE_INTERFACE_STRING(CommonUnitHz, " Hz"); +DEFINE_INTERFACE_STRING(CommonUnitKHz, " kHz"); +DEFINE_INTERFACE_STRING(CommonUnitMHz, " MHz"); + +DEFINE_INTERFACE_STRING(CommonBooleanYes, "Yes"); +DEFINE_INTERFACE_STRING(CommonBooleanNo, "No"); +DEFINE_INTERFACE_STRING(CommonBooleanOn, "On"); +DEFINE_INTERFACE_STRING(CommonBooleanOff, "Off"); // Desktop. diff --git a/util/api_table.ini b/util/api_table.ini index 472c6f3..abec80b 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -142,7 +142,6 @@ EsMutexAcquire=140 EsMutexDestroy=141 EsMutexRelease=142 EsListViewAddAllColumns=143 -EsListViewFixedItemSet=144 EsSchedulerYield=145 EsSleep=146 EsSpinlockAcquire=147 @@ -298,6 +297,7 @@ EsSliderCreate=296 EsSystemConfigurationReadString=297 EsSliderGetValue=298 EsGameControllerStatePoll=299 +EsListViewFixedItemSetString=300 EsRandomU8=301 EsRandomU64=302 EsConnectionPoll=303 @@ -312,6 +312,7 @@ EsFileDelete=311 EsBufferReadInto=312 EsListViewRemoveAll=313 EsSystemShowShutdownDialog=314 +EsListViewFixedItemSetDouble=315 EsListViewSelect=316 EsUndoClear=317 EsMessageReceive=318 @@ -421,6 +422,7 @@ EsTimerCancel=421 EsSliderSetValue=422 EsClipboardCloseAndAdd=423 EsListViewFixedItemInsert=424 +EsListViewFixedItemSetInteger=425 EsListViewFixedItemFindIndex=426 EsListViewFixedItemSelect=427 EsListViewFixedItemGetSelected=428 @@ -489,3 +491,4 @@ EsDialogGetContentArea=490 _EsDebugCommand=491 DateToLinear=492 EsRectangleContainsAll=493 +EsListViewFixedItemSetEnumStringsForColumn=494