EsButtonSetIconFromBits; icons for 2048 game

This commit is contained in:
nakst 2021-09-20 17:08:54 +01:00
parent 67c1c44184
commit 6c7ac5e749
14 changed files with 163 additions and 54 deletions

View File

@ -1,7 +1,10 @@
[general]
name=2048
icon=icon_applications_other
use_single_instance=1
[build]
source=apps/2048.cpp
[embed]
$Icons/16=res/2048_icon16.png
$Icons/32=res/2048_icon32.png

View File

@ -130,7 +130,7 @@ const EsStyle stylePanel = {
int TestCanvasMessage(EsElement *, EsMessage *message) {
if (message->type == ES_MSG_PAINT) {
size_t dataBytes;
const void *data = EsEmbeddedFileGet("test", -1, &dataBytes);
const void *data = EsBundleFind(nullptr, "test", -1, &dataBytes);
if (data) EsDrawVectorFile(message->painter, EsPainterBoundsClient(message->painter), data, dataBytes);
uint32_t cornerRadii[4] = { 10, 20, 30, 40 };

View File

@ -76,6 +76,7 @@ struct EsFileStore {
EsHandle handle;
struct {
const EsBundle *bundle;
char *path;
size_t pathBytes;
};
@ -115,6 +116,21 @@ struct Work {
EsGeneric context;
};
struct EsBundle {
const BundleHeader *base;
ptrdiff_t bytes;
};
const EsBundle bundleDefault = {
.base = (const BundleHeader *) BUNDLE_FILE_MAP_ADDRESS,
.bytes = -1,
};
const EsBundle bundleDesktop = {
.base = (const BundleHeader *) BUNDLE_FILE_DESKTOP_MAP_ADDRESS,
.bytes = -1,
};
struct {
Array<EsSystemConfigurationGroup> systemConfigurationGroups;
EsMutex systemConfigurationMutex;
@ -161,7 +177,7 @@ void UndoManagerDestroy(EsUndoManager *manager);
int TextGetStringWidth(EsElement *element, const EsTextStyle *style, const char *string, size_t stringBytes);
struct APIInstance *InstanceSetup(EsInstance *instance);
EsTextStyle TextPlanGetPrimaryStyle(EsTextPlan *plan);
EsFileStore *FileStoreCreateFromEmbeddedFile(const char *path, size_t pathBytes);
EsFileStore *FileStoreCreateFromEmbeddedFile(const EsBundle *bundle, const char *path, size_t pathBytes);
EsFileStore *FileStoreCreateFromPath(const char *path, size_t pathBytes);
EsFileStore *FileStoreCreateFromHandle(EsHandle handle);
void FileStoreCloseHandle(EsFileStore *fileStore);
@ -678,7 +694,7 @@ EsFileStore *FileStoreCreateFromHandle(EsHandle handle) {
return fileStore;
}
EsFileStore *FileStoreCreateFromEmbeddedFile(const char *name, size_t nameBytes) {
EsFileStore *FileStoreCreateFromEmbeddedFile(const EsBundle *bundle, const char *name, size_t nameBytes) {
EsFileStore *fileStore = (EsFileStore *) EsHeapAllocate(sizeof(EsFileStore) + nameBytes, false);
if (!fileStore) return nullptr;
EsMemoryZero(fileStore, sizeof(EsFileStore));
@ -687,6 +703,7 @@ EsFileStore *FileStoreCreateFromEmbeddedFile(const char *name, size_t nameBytes)
fileStore->error = ES_SUCCESS;
fileStore->path = (char *) (fileStore + 1);
fileStore->pathBytes = nameBytes;
fileStore->bundle = bundle;
EsMemoryCopy(fileStore->path, name, nameBytes);
return fileStore;
}
@ -1732,18 +1749,24 @@ void EsInstanceSetActiveUndoManager(EsInstance *_instance, EsUndoManager *manage
EsCommandSetDisabled(EsCommandByID(manager->instance, ES_COMMAND_REDO), !manager->redoStack.Length());
}
const void *EsEmbeddedFileGet(const char *_name, ptrdiff_t nameBytes, size_t *byteCount) {
const void *EsBundleFind(const EsBundle *bundle, const char *_name, ptrdiff_t nameBytes, size_t *byteCount) {
if (!bundle) {
bundle = &bundleDefault;
}
if (nameBytes == -1) {
nameBytes = EsCStringLength(_name);
}
const BundleHeader *header = (const BundleHeader *) BUNDLE_FILE_MAP_ADDRESS;
if (nameBytes > 9 && 0 == EsMemoryCompare(_name, "$Desktop/", 9)) {
header = (const BundleHeader *) BUNDLE_FILE_DESKTOP_MAP_ADDRESS;
_name += 9, nameBytes -= 9;
if (bundle->bytes != -1) {
if ((size_t) bundle->bytes < sizeof(BundleHeader)
|| (size_t) (bundle->bytes - sizeof(BundleHeader)) / sizeof(BundleFile) < bundle->base->fileCount
|| bundle->base->signature != BUNDLE_SIGNATURE || bundle->base->version != 1) {
return nullptr;
}
}
const BundleHeader *header = bundle->base;
const BundleFile *files = (const BundleFile *) (header + 1);
uint64_t name = CalculateCRC64(_name, nameBytes);
@ -1753,6 +1776,12 @@ const void *EsEmbeddedFileGet(const char *_name, ptrdiff_t nameBytes, size_t *by
*byteCount = files[i].bytes;
}
if (bundle->bytes != -1) {
if (files[i].offset >= (size_t) bundle->bytes || files[i].bytes > (size_t) (bundle->bytes - files[i].offset)) {
return nullptr;
}
}
return (const uint8_t *) header + files[i].offset;
}
}

View File

@ -1261,9 +1261,41 @@ void InstanceBlankTabCreate(EsMessage *message) {
if (application->hidden) continue;
EsButton *button = EsButtonCreate(buttonGroup, ES_CELL_H_FILL | ES_ELEMENT_NO_FOCUS_ON_CLICK, ES_STYLE_BUTTON_GROUP_ITEM, application->cName);
EsButtonSetIcon(button, (EsStandardIcon) application->iconID ?: ES_ICON_APPLICATION_DEFAULT_ICON);
button->userData = application;
if (application->iconID) {
EsButtonSetIcon(button, (EsStandardIcon) application->iconID);
} else {
EsButtonSetIcon(button, ES_ICON_APPLICATION_DEFAULT_ICON);
// TODO Load the icon asynchronously.
// TODO Load the correct icon size.
// TODO Reload the icon if the UI scale factor changes.
// TODO Cache the icon bits.
// TODO Generic icon and thumbnail cache in the API, based off the one from File Manager?
size_t fileBytes;
void *file = EsFileMap(application->cExecutable, -1, &fileBytes, ES_MAP_OBJECT_READ_ONLY);
EsBundle bundle = { .base = (const BundleHeader *) file, .bytes = (ptrdiff_t) fileBytes };
if (file) {
size_t icon32Bytes;
const void *icon32 = EsBundleFind(&bundle, EsLiteral("$Icons/32"), &icon32Bytes);
if (icon32) {
uint32_t width, height;
uint32_t *bits = (uint32_t *) EsImageLoad(icon32, icon32Bytes, &width, &height, 4);
if (bits) {
EsButtonSetIconFromBits(button, bits, width, height, width * 4);
EsHeapFree(bits);
}
}
EsObjectUnmap(file);
}
}
EsButtonOnCommand(button, [] (EsInstance *, EsElement *element, EsCommand *) {
ApplicationInstance *instance = ApplicationInstanceFindByWindowID(element->window->id);

View File

@ -295,6 +295,15 @@ struct EsButton : EsElement {
EsCommand *command;
EsCommandCallback onCommand;
EsElement *checkBuddy;
EsImageDisplay *imageDisplay;
};
struct EsImageDisplay : EsElement {
void *source;
size_t sourceBytes;
uint32_t *bits;
size_t width, height, stride;
};
struct ScrollPane {
@ -3729,6 +3738,14 @@ int ProcessButtonMessage(EsElement *element, EsMessage *message) {
ES_RECT_2S(message->painter->width, message->painter->height),
button->label, button->labelBytes, button->iconID,
(button->flags & ES_BUTTON_DROPDOWN) ? ES_DRAW_CONTENT_MARKER_DOWN_ARROW : ES_FLAGS_DEFAULT);
} else if (message->type == ES_MSG_PAINT_ICON) {
if (button->imageDisplay) {
EsRectangle imageSize = ES_RECT_2S(button->imageDisplay->width, button->imageDisplay->height);
EsRectangle bounds = EsRectangleFit(EsPainterBoundsClient(message->painter), imageSize, true);
EsImageDisplayPaint(button->imageDisplay, message->painter, bounds);
} else {
return 0;
}
} else if (message->type == ES_MSG_GET_WIDTH) {
if (!button->measurementCache.Get(message, &button->state)) {
EsTextStyle textStyle;
@ -3760,6 +3777,11 @@ int ProcessButtonMessage(EsElement *element, EsMessage *message) {
elements.FindAndDeleteSwap(button, true);
button->command->elements = elements.array;
}
if (button->imageDisplay) {
EsElementDestroy(button->imageDisplay);
button->imageDisplay = nullptr;
}
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
} else if (message->type == ES_MSG_MOUSE_LEFT_CLICK) {
if (button->flags & ES_BUTTON_CHECKBOX) {
@ -3885,10 +3907,28 @@ EsButton *EsButtonCreate(EsElement *parent, uint64_t flags, const EsStyle *style
void EsButtonSetIcon(EsButton *button, uint32_t iconID) {
EsMessageMutexCheck();
if (button->imageDisplay) {
EsElementDestroy(button->imageDisplay);
button->imageDisplay = nullptr;
}
button->iconID = iconID;
button->Repaint(true);
}
void EsButtonSetIconFromBits(EsButton *button, const uint32_t *bits, size_t width, size_t height, size_t stride) {
EsMessageMutexCheck();
if (!button->imageDisplay) {
button->imageDisplay = EsImageDisplayCreate(button);
}
if (button->imageDisplay) {
EsImageDisplayLoadBits(button->imageDisplay, bits, width, height, stride);
button->Repaint(true);
}
}
void EsButtonOnCommand(EsButton *button, EsCommandCallback onCommand, EsCommand *command) {
EsMessageMutexCheck();
@ -4991,44 +5031,43 @@ EsSplitter *EsSplitterCreate(EsElement *parent, uint64_t flags, const EsStyle *s
// clipboard
// zoom/pan
struct EsImageDisplay : EsElement {
void *source;
size_t sourceBytes;
void EsImageDisplayPaint(EsImageDisplay *display, EsPainter *painter, EsRectangle bounds) {
if (!display->bits && !display->source) {
return;
}
uint32_t *bits;
size_t width, height, stride;
};
if (!display->bits && display->source) {
uint32_t width, height;
uint8_t *bits = EsImageLoad((uint8_t *) display->source, display->sourceBytes, &width, &height, 4);
if (bits) {
display->bits = (uint32_t *) bits;
display->width = width;
display->height = height;
display->stride = width * 4;
}
if (~display->flags & UI_STATE_CHECK_VISIBLE) {
if (display->window->checkVisible.Add(display)) {
display->state |= UI_STATE_CHECK_VISIBLE;
}
}
}
EsPaintTarget source = {};
source.bits = display->bits;
source.width = display->width;
source.height = display->height;
source.stride = display->stride;
source.fullAlpha = ~display->flags & ES_IMAGE_DISPLAY_FULLY_OPAQUE;
EsDrawPaintTarget(painter, &source, bounds, ES_RECT_4(0, display->width, 0, display->height), 0xFF);
}
int ProcessImageDisplayMessage(EsElement *element, EsMessage *message) {
EsImageDisplay *display = (EsImageDisplay *) element;
if (message->type == ES_MSG_PAINT && (display->bits || display->source)) {
if (!display->bits && display->source) {
uint32_t width, height;
uint8_t *bits = EsImageLoad((uint8_t *) display->source, display->sourceBytes, &width, &height, 4);
if (bits) {
display->bits = (uint32_t *) bits;
display->width = width;
display->height = height;
display->stride = width * 4;
}
if (~display->flags & UI_STATE_CHECK_VISIBLE) {
if (display->window->checkVisible.Add(display)) {
display->state |= UI_STATE_CHECK_VISIBLE;
}
}
}
EsPaintTarget source = {};
source.bits = display->bits;
source.width = display->width;
source.height = display->height;
source.stride = display->stride;
EsDrawPaintTarget(message->painter, &source,
EsPainterBoundsInset(message->painter),
ES_RECT_4(0, display->width, 0, display->height), 0xFF);
if (message->type == ES_MSG_PAINT) {
EsImageDisplayPaint(display, message->painter, EsPainterBoundsInset(message->painter));
} else if (message->type == ES_MSG_GET_WIDTH) {
message->measure.width = display->width;
} else if (message->type == ES_MSG_GET_HEIGHT) {

View File

@ -20,6 +20,7 @@ opaque_type EsUndoManager none;
opaque_type EsHeap none;
opaque_type EsFileStore none;
opaque_type EsUserTask none;
opaque_type EsBundle none;
type_name uint8_t EsNodeType;
type_name intptr_t EsError;
@ -570,6 +571,7 @@ define ES_LIST_DISPLAY_MARKER_TYPE_MASK (0xFF << 0)
define ES_IMAGE_DISPLAY_DECODE_WHEN_NEEDED (1 << 0) // The image is only kept in its decoded state when the display is on-screen.
define ES_IMAGE_DISPLAY_MANUAL_SIZE (1 << 1) // The display will be manually sized; its size does not depend on the loaded image.
define ES_IMAGE_DISPLAY_FULLY_OPAQUE (1 << 2) // The loaded image will always be fully opaque.
define ES_COMMAND_SYSTEM_START (0xF0000000)
define ES_COMMAND_DELETE (0xF0000001)
@ -1947,7 +1949,7 @@ function void EsINIZeroTerminate(EsINIState *s);
// File systems.
function const void *EsEmbeddedFileGet(STRING name, size_t *byteCount = ES_NULL);
function const void *EsBundleFind(const EsBundle *bundle, STRING name, size_t *byteCount = ES_NULL); // Pass null as the bundle to use the current application's bundle.
function ptrdiff_t EsDirectoryEnumerateChildren(STRING path, EsDirectoryChild **buffer); // Free buffer with EsHeapFree. Returns number of children.
@ -2390,6 +2392,7 @@ private function void _EsUISetFont(EsFontFamily id);
function EsButton *EsButtonCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL, STRING label = BLANK_STRING);
function void EsButtonSetIcon(EsButton *button, uint32_t iconID);
function void EsButtonSetIconFromBits(EsButton *button, const uint32_t *bits, size_t width, size_t height, size_t stride);
function void EsButtonSetCheck(EsButton *button, EsCheckState checkState = ES_CHECK_CHECKED, bool sendUpdatedMessage = true);
function EsCheckState EsButtonGetCheck(EsButton *button);
function void EsButtonOnCommand(EsButton *button, EsCommandCallback callback, EsCommand *command = ES_NULL); // TODO Public property?
@ -2450,6 +2453,7 @@ function void EsIconDisplaySetIcon(EsIconDisplay *display, uint32_t iconID);
function EsImageDisplay *EsImageDisplayCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL);
function void EsImageDisplayLoadBits(EsImageDisplay *display, const uint32_t *bits, size_t width, size_t height, size_t stride);
function void EsImageDisplayLoadFromMemory(EsImageDisplay *display, const void *buffer, size_t bufferBytes);
function void EsImageDisplayPaint(EsImageDisplay *display, EsPainter *painter, EsRectangle bounds);
function EsTextDisplay *EsTextDisplayCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL, STRING label = BLANK_STRING);
function void EsTextDisplaySetContents(EsTextDisplay *display, STRING contents = BLANK_STRING);

View File

@ -352,7 +352,7 @@ void *EsFileStoreReadAll(EsFileStore *file, size_t *fileSize) {
return EsFileReadAll(file->path, file->pathBytes, fileSize, &file->error);
} else if (file->type == FILE_STORE_EMBEDDED_FILE) {
size_t _fileSize;
const void *data = EsEmbeddedFileGet(file->path, file->pathBytes, &_fileSize);
const void *data = EsBundleFind(file->bundle, file->path, file->pathBytes, &_fileSize);
void *copy = EsHeapAllocate(_fileSize, false);
if (!copy) return nullptr;
if (fileSize) *fileSize = _fileSize;
@ -404,7 +404,7 @@ EsFileOffsetDifference EsFileStoreGetSize(EsFileStore *file) {
}
} else if (file->type == FILE_STORE_EMBEDDED_FILE) {
size_t size;
EsEmbeddedFileGet(file->path, file->pathBytes, &size);
EsBundleFind(file->bundle, file->path, file->pathBytes, &size);
return size;
} else {
EsAssert(false);
@ -421,7 +421,7 @@ void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flags) {
} else if (file->type == FILE_STORE_PATH) {
return EsFileMap(file->path, file->pathBytes, fileSize, flags);
} else if (file->type == FILE_STORE_EMBEDDED_FILE) {
return (void *) EsEmbeddedFileGet(file->path, file->pathBytes, fileSize);
return (void *) EsBundleFind(file->bundle, file->path, file->pathBytes, fileSize);
} else {
EsAssert(false);
return nullptr;

View File

@ -512,7 +512,7 @@ void FontInitialise() {
size_t fileIndex = weight - 1 + italic * 9;
if (item->valueBytes && item->value[0] == ':') {
entry.files[fileIndex] = FileStoreCreateFromEmbeddedFile(item->value + 1, item->valueBytes - 1);
entry.files[fileIndex] = FileStoreCreateFromEmbeddedFile(&bundleDesktop, item->value + 1, item->valueBytes - 1);
} else {
entry.files[fileIndex] = FileStoreCreateFromPath(item->value, item->valueBytes);
}
@ -1373,7 +1373,7 @@ bool EsDrawStandardIcon(EsPainter *painter, uint32_t id, int size, EsRectangle r
if (!cacheEntry->data) {
if (!iconManagement.standardPack) {
iconManagement.standardPack = (const uint8_t *) EsEmbeddedFileGet(EsLiteral("$Desktop/Icons.dat"), &iconManagement.standardPackSize);
iconManagement.standardPack = (const uint8_t *) EsBundleFind(&bundleDesktop, EsLiteral("Icons.dat"), &iconManagement.standardPackSize);
}
iconManagement.buffer = (char *) EsHeapAllocate((iconManagement.bufferAllocated = 131072), false);

View File

@ -1318,7 +1318,7 @@ const char *GetConstantString(const char *cKey) {
bool ThemeInitialise() {
EsBuffer data = {};
data.in = (const uint8_t *) EsEmbeddedFileGet(EsLiteral("$Desktop/Theme.dat"), &data.bytes);
data.in = (const uint8_t *) EsBundleFind(&bundleDesktop, EsLiteral("Theme.dat"), &data.bytes);
const ThemeHeader *header = (const ThemeHeader *) EsBufferRead(&data, sizeof(ThemeHeader));

BIN
res/2048_icon16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

BIN
res/2048_icon32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -33,7 +33,7 @@ DEFINE_INTERFACE_STRING(CommonFileMakeCopy, "Make a copy");
DEFINE_INTERFACE_STRING(CommonFileVersionHistory, "Version history" ELLIPSIS);
DEFINE_INTERFACE_STRING(CommonFileShowInFileManager, "Show in File Manager" ELLIPSIS);
DEFINE_INTERFACE_STRING(CommonFileMenuFileSize, "Size:");
DEFINE_INTERFACE_STRING(CommonFileMenuFileLocation, "Location:");
DEFINE_INTERFACE_STRING(CommonFileMenuFileLocation, "Where:");
DEFINE_INTERFACE_STRING(CommonFileUnchanged, "(All changes saved.)");
DEFINE_INTERFACE_STRING(CommonSearchOpen, "Search");

View File

@ -366,7 +366,7 @@ EsElementStartTransition=364
EsToolbarAddFileMenu=365
EsFileWriteAllFromHandle=366
EsFileWriteAllGatherFromHandle=367
EsEmbeddedFileGet=368
EsButtonSetIconFromBits=368
EsFileStoreWriteAll=369
EsInstanceOpenComplete=370
EsInstanceSaveComplete=371
@ -378,6 +378,7 @@ EsMountPointEnumerate=376
EsMountPointGetVolumeInformation=377
EsListViewInvalidateAll=378
EsListViewGetFocusedItem=379
EsBundleFind=380
EsPathQueryInformation=381
EsListViewCreateInlineTextbox=382
EsTextboxStartEdit=383
@ -458,3 +459,4 @@ EsPanelRadioGroupGetChecked=457
EsTextboxEnableSmartQuotes=458
EsBufferWriteInt8=459
EsInstanceGetStartupRequest=460
EsImageDisplayPaint=461

View File

@ -858,7 +858,7 @@ void OutputSystemConfiguration() {
FilePrintFormat(file, "%s=|Fonts:/%.*s.dat\n", fontLines[i].key, (int) fontLines[i].valueBytes - 4, fontLines[i].value);
#endif
} else {
FilePrintFormat(file, "%s=:$Desktop/%s\n", fontLines[i].key, fontLines[i].value);
FilePrintFormat(file, "%s=:%s\n", fontLines[i].key, fontLines[i].value);
}
} else {
size_t bytes = EsINIFormat(fontLines + i, buffer, sizeof(buffer));