mirror of https://gitlab.com/nakst/essence
saving images from image editor; auto update thumbnails in file manager
This commit is contained in:
parent
b4dfe96d85
commit
9a7c7106f4
|
@ -355,14 +355,19 @@ void FolderDetachInstance(Instance *instance) {
|
||||||
folder->referenceCount--;
|
folder->referenceCount--;
|
||||||
|
|
||||||
if (!folder->referenceCount) {
|
if (!folder->referenceCount) {
|
||||||
EsAssert(!folder->attachedInstances.Length());
|
if (folder->refreshing) {
|
||||||
foldersWithNoAttachedInstances.Add(folder);
|
loadedFolders.FindAndDeleteSwap(folder, true);
|
||||||
|
FolderDestroy(folder);
|
||||||
|
} else {
|
||||||
|
EsAssert(!folder->attachedInstances.Length());
|
||||||
|
foldersWithNoAttachedInstances.Add(folder);
|
||||||
|
|
||||||
if (foldersWithNoAttachedInstances.Length() > MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES) {
|
if (foldersWithNoAttachedInstances.Length() > MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES) {
|
||||||
Folder *leastRecentlyUsed = foldersWithNoAttachedInstances[0];
|
Folder *leastRecentlyUsed = foldersWithNoAttachedInstances[0];
|
||||||
loadedFolders.FindAndDeleteSwap(leastRecentlyUsed, true);
|
loadedFolders.FindAndDeleteSwap(leastRecentlyUsed, true);
|
||||||
foldersWithNoAttachedInstances.Delete(0);
|
foldersWithNoAttachedInstances.Delete(0);
|
||||||
FolderDestroy(leastRecentlyUsed);
|
FolderDestroy(leastRecentlyUsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,6 +424,8 @@ void FolderAddEntryAndUpdateInstances(Folder *folder, const char *name, size_t n
|
||||||
FolderEntry *entry = FolderAddEntry(folder, name, nameBytes, information, id);
|
FolderEntry *entry = FolderAddEntry(folder, name, nameBytes, information, id);
|
||||||
ListEntry listEntry = { .entry = entry };
|
ListEntry listEntry = { .entry = entry };
|
||||||
|
|
||||||
|
ThumbnailGenerateIfNeeded(folder, entry, false, true /* modified */);
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < folder->attachedInstances.Length(); i++) {
|
for (uintptr_t i = 0; i < folder->attachedInstances.Length(); i++) {
|
||||||
Instance *instance = folder->attachedInstances[i];
|
Instance *instance = folder->attachedInstances[i];
|
||||||
listEntry.selected = instance == selectItem;
|
listEntry.selected = instance == selectItem;
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
#define ES_INSTANCE_TYPE Instance
|
#define ES_INSTANCE_TYPE Instance
|
||||||
#include <essence.h>
|
#include <essence.h>
|
||||||
#include <shared/strings.cpp>
|
#include <shared/strings.cpp>
|
||||||
|
|
||||||
#include <shared/hash_table.cpp>
|
#include <shared/hash_table.cpp>
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#undef IMPLEMENTATION
|
|
||||||
|
|
||||||
// TODO Possible candidates for moving in the core API:
|
// TODO Possible candidates for moving in the core API:
|
||||||
// - String/paths utils
|
// - String/paths utils
|
||||||
|
@ -239,6 +235,7 @@ void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename)
|
||||||
FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes, EsDirectoryChild *information, uint64_t id = 0);
|
FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes, EsDirectoryChild *information, uint64_t id = 0);
|
||||||
void FolderAddEntries(Folder *folder, EsDirectoryChild *buffer, size_t entryCount);
|
void FolderAddEntries(Folder *folder, EsDirectoryChild *buffer, size_t entryCount);
|
||||||
uint32_t NamespaceDefaultGetFileType(String);
|
uint32_t NamespaceDefaultGetFileType(String);
|
||||||
|
void ThumbnailGenerateIfNeeded(Folder *folder, FolderEntry *entry, bool fromFolderRename, bool modified);
|
||||||
|
|
||||||
Array<Drive> drives;
|
Array<Drive> drives;
|
||||||
EsMutex drivesMutex;
|
EsMutex drivesMutex;
|
||||||
|
|
|
@ -545,7 +545,7 @@ void ThumbnailResize(uint32_t *bits, uint32_t originalWidth, uint32_t originalHe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemGenerateThumbnailTask(Instance *, Task *task) {
|
void ThumbnailGenerateTask(Instance *, Task *task) {
|
||||||
EsMessageMutexAcquire();
|
EsMessageMutexAcquire();
|
||||||
Thumbnail *thumbnail = thumbnailCache.Get(&task->id);
|
Thumbnail *thumbnail = thumbnailCache.Get(&task->id);
|
||||||
bool cancelTask = !thumbnail || thumbnail->referenceCount == 0;
|
bool cancelTask = !thumbnail || thumbnail->referenceCount == 0;
|
||||||
|
@ -619,7 +619,7 @@ void ListItemGenerateThumbnailTask(Instance *, Task *task) {
|
||||||
EsMessageMutexRelease();
|
EsMessageMutexRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemGenerateThumbnailTaskComplete(Instance *, Task *task) {
|
void ThumbnailGenerateTaskComplete(Instance *, Task *task) {
|
||||||
Thumbnail *thumbnail = thumbnailCache.Get(&task->id);
|
Thumbnail *thumbnail = thumbnailCache.Get(&task->id);
|
||||||
|
|
||||||
if (thumbnail) {
|
if (thumbnail) {
|
||||||
|
@ -637,11 +637,47 @@ void ListItemGenerateThumbnailTaskComplete(Instance *, Task *task) {
|
||||||
StringDestroy(&task->string);
|
StringDestroy(&task->string);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thumbnail *ListItemGetThumbnail(EsElement *element) {
|
void ThumbnailGenerateIfNeeded(Folder *folder, FolderEntry *entry, bool fromFolderRename, bool modified) {
|
||||||
Instance *instance = element->instance;
|
FileType *fileType = FolderEntryGetType(folder, entry);
|
||||||
ListEntry *entry = &instance->listContents[EsListViewGetIndexFromItem(element)];
|
|
||||||
Thumbnail *thumbnail = thumbnailCache.Get(&entry->entry->id);
|
if (!fileType->hasThumbnailGenerator) {
|
||||||
return thumbnail;
|
return; // The file type does not support thumbnail generation.
|
||||||
|
}
|
||||||
|
|
||||||
|
Thumbnail *thumbnail;
|
||||||
|
|
||||||
|
// TODO Remove from LRU if needed.
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
thumbnail = thumbnailCache.Get(&entry->id);
|
||||||
|
|
||||||
|
if (!thumbnail || (!thumbnail->generatingTasksInProgress && !thumbnail->bits)) {
|
||||||
|
return; // The thumbnail is not in use.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thumbnail = thumbnailCache.Put(&entry->id);
|
||||||
|
|
||||||
|
if (!fromFolderRename) {
|
||||||
|
thumbnail->referenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((thumbnail->generatingTasksInProgress && !fromFolderRename) || thumbnail->bits) {
|
||||||
|
return; // The thumbnail is already being/has already been generated.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnail->generatingTasksInProgress++;
|
||||||
|
|
||||||
|
String path = StringAllocateAndFormat("%s%s", STRFMT(folder->path), STRFMT(entry->GetInternalName()));
|
||||||
|
|
||||||
|
Task task = {
|
||||||
|
.string = path,
|
||||||
|
.id = entry->id,
|
||||||
|
.callback = ThumbnailGenerateTask,
|
||||||
|
.then = ThumbnailGenerateTaskComplete,
|
||||||
|
};
|
||||||
|
|
||||||
|
NonBlockingTaskQueue(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename) {
|
void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename) {
|
||||||
|
@ -651,38 +687,14 @@ void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename)
|
||||||
return; // The current view does not display thumbnails.
|
return; // The current view does not display thumbnails.
|
||||||
}
|
}
|
||||||
|
|
||||||
ListEntry *listEntry = &instance->listContents[index];
|
ThumbnailGenerateIfNeeded(instance->folder, instance->listContents[index].entry, fromFolderRename, false /* not modified */);
|
||||||
FolderEntry *entry = listEntry->entry;
|
}
|
||||||
FileType *fileType = FolderEntryGetType(instance->folder, entry);
|
|
||||||
|
|
||||||
if (!fileType->hasThumbnailGenerator) {
|
Thumbnail *ListItemGetThumbnail(EsElement *element) {
|
||||||
return; // The file type does not support thumbnail generation.
|
Instance *instance = element->instance;
|
||||||
}
|
ListEntry *entry = &instance->listContents[EsListViewGetIndexFromItem(element)];
|
||||||
|
Thumbnail *thumbnail = thumbnailCache.Get(&entry->entry->id);
|
||||||
Thumbnail *thumbnail = thumbnailCache.Put(&entry->id);
|
return thumbnail;
|
||||||
|
|
||||||
if (!fromFolderRename) {
|
|
||||||
thumbnail->referenceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Remove from LRU if needed.
|
|
||||||
|
|
||||||
if ((thumbnail->generatingTasksInProgress && !fromFolderRename) || thumbnail->bits) {
|
|
||||||
return; // The thumbnail is already being/has already been generated.
|
|
||||||
}
|
|
||||||
|
|
||||||
thumbnail->generatingTasksInProgress++;
|
|
||||||
|
|
||||||
String path = StringAllocateAndFormat("%s%s", STRFMT(instance->path), STRFMT(entry->GetInternalName()));
|
|
||||||
|
|
||||||
Task task = {
|
|
||||||
.string = path,
|
|
||||||
.id = entry->id,
|
|
||||||
.callback = ListItemGenerateThumbnailTask,
|
|
||||||
.then = ListItemGenerateThumbnailTaskComplete,
|
|
||||||
};
|
|
||||||
|
|
||||||
NonBlockingTaskQueue(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ListItemMessage(EsElement *element, EsMessage *message) {
|
int ListItemMessage(EsElement *element, EsMessage *message) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#define ES_INSTANCE_TYPE Instance
|
#define ES_INSTANCE_TYPE Instance
|
||||||
#include <essence.h>
|
#include <essence.h>
|
||||||
|
#include <shared/array.cpp>
|
||||||
|
#include <shared/strings.cpp>
|
||||||
|
|
||||||
// TODO Previewing font files from the database.
|
// TODO Previewing font files from the database.
|
||||||
// TODO Installing fonts.
|
// TODO Installing fonts.
|
||||||
|
@ -7,11 +9,6 @@
|
||||||
// TODO Searching/filtering fonts.
|
// TODO Searching/filtering fonts.
|
||||||
// TODO Single instance.
|
// TODO Single instance.
|
||||||
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#include <shared/strings.cpp>
|
|
||||||
|
|
||||||
#define SETTINGS_FILE "|Settings:/Default.ini"
|
#define SETTINGS_FILE "|Settings:/Default.ini"
|
||||||
|
|
||||||
struct Instance : EsInstance {
|
struct Instance : EsInstance {
|
||||||
|
|
|
@ -15,14 +15,17 @@
|
||||||
|
|
||||||
#define ES_INSTANCE_TYPE Instance
|
#define ES_INSTANCE_TYPE Instance
|
||||||
#include <essence.h>
|
#include <essence.h>
|
||||||
|
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
#include <shared/strings.cpp>
|
#include <shared/strings.cpp>
|
||||||
|
|
||||||
#ifdef OS_ESSENCE
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#define IMPLEMENTATION
|
#define STBI_WRITE_NO_STDIO
|
||||||
#include <shared/array.cpp>
|
#define STBIW_MEMMOVE EsCRTmemmove
|
||||||
#endif
|
#define STBIW_MALLOC(sz) EsCRTmalloc(sz)
|
||||||
|
#define STBIW_REALLOC(p,newsz) EsCRTrealloc(p,newsz)
|
||||||
|
#define STBIW_FREE(p) EsCRTfree(p)
|
||||||
|
#define STBIW_ASSERT EsAssert
|
||||||
|
#include <shared/stb_image_write.h>
|
||||||
|
|
||||||
#define TILE_SIZE (128)
|
#define TILE_SIZE (128)
|
||||||
|
|
||||||
|
@ -65,6 +68,12 @@ struct Instance : EsInstance {
|
||||||
bool dragged;
|
bool dragged;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EsInstanceClassEditorSettings editorSettings = {
|
||||||
|
INTERFACE_STRING(ImageEditorNewFileName),
|
||||||
|
INTERFACE_STRING(ImageEditorNewDocument),
|
||||||
|
ES_ICON_IMAGE_X_GENERIC,
|
||||||
|
};
|
||||||
|
|
||||||
const EsStyle styleBitmapSizeTextbox = {
|
const EsStyle styleBitmapSizeTextbox = {
|
||||||
.inherit = ES_STYLE_TEXTBOX_BORDERED_SINGLE_COMPACT,
|
.inherit = ES_STYLE_TEXTBOX_BORDERED_SINGLE_COMPACT,
|
||||||
|
|
||||||
|
@ -433,7 +442,7 @@ int CanvasMessage(EsElement *element, EsMessage *message) {
|
||||||
EsRectangle rectangle = instance->modifiedBounds;
|
EsRectangle rectangle = instance->modifiedBounds;
|
||||||
rectangle.l += painter->offsetX, rectangle.r += painter->offsetX;
|
rectangle.l += painter->offsetX, rectangle.r += painter->offsetX;
|
||||||
rectangle.t += painter->offsetY, rectangle.b += painter->offsetY;
|
rectangle.t += painter->offsetY, rectangle.b += painter->offsetY;
|
||||||
EsDrawBlock(painter, rectangle, 0xFF000000 | EsColorWellGetRGB(instance->colorWell));
|
EsDrawBlock(painter, EsRectangleIntersection(rectangle, area), 0xFF000000 | EsColorWellGetRGB(instance->colorWell));
|
||||||
}
|
}
|
||||||
} else if ((message->type == ES_MSG_MOUSE_LEFT_DRAG || message->type == ES_MSG_MOUSE_MOVED)
|
} else if ((message->type == ES_MSG_MOUSE_LEFT_DRAG || message->type == ES_MSG_MOUSE_MOVED)
|
||||||
&& EsMouseIsLeftHeld() && instance->commandBrush.check == ES_CHECK_CHECKED) {
|
&& EsMouseIsLeftHeld() && instance->commandBrush.check == ES_CHECK_CHECKED) {
|
||||||
|
@ -691,6 +700,7 @@ void MenuImage(Instance *instance, EsElement *element, EsCommand *) {
|
||||||
void InstanceCreate(EsMessage *message) {
|
void InstanceCreate(EsMessage *message) {
|
||||||
Instance *instance = EsInstanceCreate(message, INTERFACE_STRING(ImageEditorTitle));
|
Instance *instance = EsInstanceCreate(message, INTERFACE_STRING(ImageEditorTitle));
|
||||||
EsElement *toolbar = EsWindowGetToolbar(instance->window);
|
EsElement *toolbar = EsWindowGetToolbar(instance->window);
|
||||||
|
EsInstanceSetClassEditor(instance, &editorSettings);
|
||||||
|
|
||||||
// Register commands.
|
// Register commands.
|
||||||
|
|
||||||
|
@ -706,6 +716,7 @@ void InstanceCreate(EsMessage *message) {
|
||||||
|
|
||||||
EsButton *button;
|
EsButton *button;
|
||||||
|
|
||||||
|
EsToolbarAddFileMenu(toolbar);
|
||||||
button = EsButtonCreate(toolbar, ES_BUTTON_DROPDOWN, ES_STYLE_PUSH_BUTTON_TOOLBAR_BIG, INTERFACE_STRING(ImageEditorImage));
|
button = EsButtonCreate(toolbar, ES_BUTTON_DROPDOWN, ES_STYLE_PUSH_BUTTON_TOOLBAR_BIG, INTERFACE_STRING(ImageEditorImage));
|
||||||
EsButtonSetIcon(button, ES_ICON_IMAGE_X_GENERIC);
|
EsButtonSetIcon(button, ES_ICON_IMAGE_X_GENERIC);
|
||||||
button->accessKey = 'I';
|
button->accessKey = 'I';
|
||||||
|
@ -755,7 +766,7 @@ void InstanceCreate(EsMessage *message) {
|
||||||
|
|
||||||
EsPanel *section = EsPanelCreate(toolbar, ES_PANEL_HORIZONTAL);
|
EsPanel *section = EsPanelCreate(toolbar, ES_PANEL_HORIZONTAL);
|
||||||
EsTextDisplayCreate(section, ES_FLAGS_DEFAULT, 0, INTERFACE_STRING(ImageEditorPropertyColor));
|
EsTextDisplayCreate(section, ES_FLAGS_DEFAULT, 0, INTERFACE_STRING(ImageEditorPropertyColor));
|
||||||
instance->colorWell = EsColorWellCreate(section, ES_FLAGS_DEFAULT, 0);
|
instance->colorWell = EsColorWellCreate(section, ES_FLAGS_DEFAULT, 0xFFFF0000);
|
||||||
instance->colorWell->accessKey = 'C';
|
instance->colorWell->accessKey = 'C';
|
||||||
EsSpacerCreate(toolbar, ES_FLAGS_DEFAULT, 0, 5, 0);
|
EsSpacerCreate(toolbar, ES_FLAGS_DEFAULT, 0, 5, 0);
|
||||||
|
|
||||||
|
@ -790,7 +801,22 @@ void InstanceCreate(EsMessage *message) {
|
||||||
ImageCopyFromPaintTarget(instance, &instance->image, painter.clip);
|
ImageCopyFromPaintTarget(instance, &instance->image, painter.clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteCallback(void *context, void *data, int size) {
|
||||||
|
EsBufferWrite((EsBuffer *) context, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapRedAndBlueChannels(uint32_t *bits, size_t width, size_t height, size_t stride) {
|
||||||
|
for (uintptr_t i = 0; i < height; i++) {
|
||||||
|
for (uintptr_t j = 0; j < width; j++) {
|
||||||
|
uint32_t *pixel = &bits[i * stride / 4 + j];
|
||||||
|
*pixel = (*pixel & 0xFF00FF00) | (((*pixel >> 16) | (*pixel << 16)) & 0x00FF00FF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _start() {
|
void _start() {
|
||||||
|
_init();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
EsMessage *message = EsMessageReceive();
|
EsMessage *message = EsMessageReceive();
|
||||||
|
|
||||||
|
@ -831,6 +857,47 @@ void _start() {
|
||||||
|
|
||||||
EsHeapFree(bits);
|
EsHeapFree(bits);
|
||||||
EsInstanceOpenComplete(message, true);
|
EsInstanceOpenComplete(message, true);
|
||||||
|
} else if (message->type == ES_MSG_INSTANCE_SAVE) {
|
||||||
|
Instance *instance = message->instanceSave.instance;
|
||||||
|
|
||||||
|
uintptr_t extensionOffset = message->instanceSave.nameBytes;
|
||||||
|
|
||||||
|
while (extensionOffset) {
|
||||||
|
if (message->instanceSave.name[extensionOffset - 1] == '.') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
extensionOffset--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *extension = extensionOffset ? message->instanceSave.name + extensionOffset : "png";
|
||||||
|
size_t extensionBytes = extensionOffset ? message->instanceSave.nameBytes - extensionOffset : 3;
|
||||||
|
|
||||||
|
uint32_t *bits;
|
||||||
|
size_t width, height, stride;
|
||||||
|
EsPaintTargetStartDirectAccess(instance->bitmap, &bits, &width, &height, &stride);
|
||||||
|
EsAssert(stride == width * 4); // TODO Other strides.
|
||||||
|
SwapRedAndBlueChannels(bits, width, height, stride); // stbi_write uses the other order. We swap back below.
|
||||||
|
|
||||||
|
uint8_t _buffer[4096];
|
||||||
|
EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) };
|
||||||
|
buffer.fileStore = message->instanceSave.file;
|
||||||
|
|
||||||
|
if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpg"))
|
||||||
|
|| 0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpeg"))) {
|
||||||
|
stbi_write_jpg_to_func(WriteCallback, &buffer, width, height, 4, bits, 90);
|
||||||
|
} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("bmp"))) {
|
||||||
|
stbi_write_bmp_to_func(WriteCallback, &buffer, width, height, 4, bits);
|
||||||
|
} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("tga"))) {
|
||||||
|
stbi_write_tga_to_func(WriteCallback, &buffer, width, height, 4, bits);
|
||||||
|
} else {
|
||||||
|
stbi_write_png_to_func(WriteCallback, &buffer, width, height, 4, bits, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwapRedAndBlueChannels(bits, width, height, stride); // Swap back.
|
||||||
|
EsBufferFlushToFileStore(&buffer);
|
||||||
|
EsPaintTargetEndDirectAccess(instance->bitmap);
|
||||||
|
EsInstanceSaveComplete(message, true);
|
||||||
} else if (message->type == ES_MSG_INSTANCE_DESTROY) {
|
} else if (message->type == ES_MSG_INSTANCE_DESTROY) {
|
||||||
Instance *instance = message->instanceDestroy.instance;
|
Instance *instance = message->instanceDestroy.instance;
|
||||||
EsPaintTargetDestroy(instance->bitmap);
|
EsPaintTargetDestroy(instance->bitmap);
|
||||||
|
|
|
@ -11,13 +11,9 @@
|
||||||
#include <shared/hash.cpp>
|
#include <shared/hash.cpp>
|
||||||
#include <shared/strings.cpp>
|
#include <shared/strings.cpp>
|
||||||
#include <shared/partitions.cpp>
|
#include <shared/partitions.cpp>
|
||||||
|
#include <shared/array.cpp>
|
||||||
#include <ports/lzma/LzmaDec.c>
|
#include <ports/lzma/LzmaDec.c>
|
||||||
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#undef IMPLEMENTATION
|
|
||||||
|
|
||||||
#define Log(...)
|
#define Log(...)
|
||||||
// TODO Error handling.
|
// TODO Error handling.
|
||||||
#define exit(x) EsAssert(false)
|
#define exit(x) EsAssert(false)
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
#define ES_INSTANCE_TYPE Instance
|
#define ES_INSTANCE_TYPE Instance
|
||||||
#include <essence.h>
|
#include <essence.h>
|
||||||
|
|
||||||
#include <shared/strings.cpp>
|
#include <shared/strings.cpp>
|
||||||
|
#include <shared/array.cpp>
|
||||||
#include <ports/md4c/md4c.c>
|
#include <ports/md4c/md4c.c>
|
||||||
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#undef IMPLEMENTATION
|
|
||||||
|
|
||||||
// TODO Inline code background?
|
// TODO Inline code background?
|
||||||
|
|
||||||
// TODO When resizing the window, maintain the scroll position.
|
// TODO When resizing the window, maintain the scroll position.
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
#define ES_INSTANCE_TYPE Instance
|
#define ES_INSTANCE_TYPE Instance
|
||||||
#include <essence.h>
|
#include <essence.h>
|
||||||
|
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#undef IMPLEMENTATION
|
|
||||||
|
|
||||||
// TODO Single instance.
|
// TODO Single instance.
|
||||||
// TODO Sorting lists.
|
// TODO Sorting lists.
|
||||||
|
|
|
@ -9,9 +9,10 @@
|
||||||
|
|
||||||
#ifdef USE_STB_IMAGE
|
#ifdef USE_STB_IMAGE
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#define STBI_MALLOC(sz) EsCRTmalloc(sz)
|
#define STBI_MALLOC(sz) EsCRTmalloc(sz)
|
||||||
#define STBI_REALLOC(p,newsz) EsCRTrealloc(p,newsz)
|
#define STBI_REALLOC(p,newsz) EsCRTrealloc(p,newsz)
|
||||||
#define STBI_FREE(p) EsCRTfree(p)
|
#define STBI_FREE(p) EsCRTfree(p)
|
||||||
|
#define STBI_ASSERT(x) EsAssert(x)
|
||||||
#define STBI_NO_STDIO
|
#define STBI_NO_STDIO
|
||||||
#define STBI_ONLY_PNG
|
#define STBI_ONLY_PNG
|
||||||
#define STBI_ONLY_JPEG
|
#define STBI_ONLY_JPEG
|
||||||
|
@ -37,10 +38,6 @@
|
||||||
#include <shared/strings.cpp>
|
#include <shared/strings.cpp>
|
||||||
#include <shared/common.cpp>
|
#include <shared/common.cpp>
|
||||||
|
|
||||||
#define IMPLEMENTATION
|
|
||||||
#include <shared/array.cpp>
|
|
||||||
#undef IMPLEMENTATION
|
|
||||||
|
|
||||||
struct EnumString { const char *cName; int value; };
|
struct EnumString { const char *cName; int value; };
|
||||||
#include <bin/enum_strings_array.h>
|
#include <bin/enum_strings_array.h>
|
||||||
|
|
||||||
|
@ -970,6 +967,10 @@ EsMessage *EsMessageReceive() {
|
||||||
m.instanceSave.file->handles = 1;
|
m.instanceSave.file->handles = 1;
|
||||||
m.instanceSave.instance = InstanceFromWindowID(message.message.tabOperation.id);
|
m.instanceSave.instance = InstanceFromWindowID(message.message.tabOperation.id);
|
||||||
|
|
||||||
|
APIInstance *instance = (APIInstance *) m.instanceSave.instance->_private;
|
||||||
|
m.instanceSave.name = instance->startupInformation->filePath;
|
||||||
|
m.instanceSave.nameBytes = instance->startupInformation->filePathBytes;
|
||||||
|
|
||||||
if (m.instanceSave.file->error == ES_SUCCESS) {
|
if (m.instanceSave.file->error == ES_SUCCESS) {
|
||||||
EsMemoryCopy(&message.message, &m, sizeof(EsMessage));
|
EsMemoryCopy(&message.message, &m, sizeof(EsMessage));
|
||||||
return &message.message;
|
return &message.message;
|
||||||
|
|
|
@ -2521,14 +2521,18 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
char *oldPath = EsStringAllocateAndFormat(&oldPathBytes, "%s", document->pathBytes, document->path);
|
char *oldPath = EsStringAllocateAndFormat(&oldPathBytes, "%s", document->pathBytes, document->path);
|
||||||
char *newPath = EsStringAllocateAndFormat(&newPathBytes, "%s/%s", folderBytes, document->path, newNameBytes, newName);
|
char *newPath = EsStringAllocateAndFormat(&newPathBytes, "%s/%s", folderBytes, document->path, newNameBytes, newName);
|
||||||
|
|
||||||
EsMessage m = {};
|
if (oldPathBytes == newPathBytes && 0 == EsMemoryCompare(oldPath, newPath, oldPathBytes)) {
|
||||||
m.type = ES_MSG_INSTANCE_RENAME_RESPONSE;
|
// Same name.
|
||||||
m.tabOperation.id = instance->embeddedWindowID;
|
} else {
|
||||||
m.tabOperation.error = EsPathMove(oldPath, oldPathBytes, newPath, newPathBytes);
|
EsMessage m = {};
|
||||||
EsMessagePostRemote(instance->process->handle, &m);
|
m.type = ES_MSG_INSTANCE_RENAME_RESPONSE;
|
||||||
|
m.tabOperation.id = instance->embeddedWindowID;
|
||||||
|
m.tabOperation.error = EsPathMove(oldPath, oldPathBytes, newPath, newPathBytes);
|
||||||
|
EsMessagePostRemote(instance->process->handle, &m);
|
||||||
|
|
||||||
if (m.tabOperation.error == ES_SUCCESS) {
|
if (m.tabOperation.error == ES_SUCCESS) {
|
||||||
InstanceAnnouncePathMoved(nullptr, oldPath, oldPathBytes, newPath, newPathBytes);
|
InstanceAnnouncePathMoved(nullptr, oldPath, oldPathBytes, newPath, newPathBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EsHeapFree(oldPath);
|
EsHeapFree(oldPath);
|
||||||
|
|
|
@ -5174,7 +5174,7 @@ int FileMenuNameTextboxMessage(EsElement *element, EsMessage *message) {
|
||||||
if (message->type == ES_MSG_TEXTBOX_EDIT_END) {
|
if (message->type == ES_MSG_TEXTBOX_EDIT_END) {
|
||||||
APIInstance *instance = (APIInstance *) element->instance->_private;
|
APIInstance *instance = (APIInstance *) element->instance->_private;
|
||||||
|
|
||||||
if (!message->endEdit.rejected) {
|
if (!message->endEdit.rejected && !message->endEdit.unchanged) {
|
||||||
size_t newNameBytes;
|
size_t newNameBytes;
|
||||||
char *newName = EsTextboxGetContents(instance->fileMenuNameTextbox, &newNameBytes);
|
char *newName = EsTextboxGetContents(instance->fileMenuNameTextbox, &newNameBytes);
|
||||||
uint8_t *buffer = (uint8_t *) EsHeapAllocate(1 + newNameBytes, false);
|
uint8_t *buffer = (uint8_t *) EsHeapAllocate(1 + newNameBytes, false);
|
||||||
|
|
|
@ -1691,7 +1691,7 @@ struct EsMessageGetBreadcrumb {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EsMessageEndEdit {
|
struct EsMessageEndEdit {
|
||||||
bool rejected;
|
bool rejected, unchanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Instance messages.
|
// Instance messages.
|
||||||
|
@ -1706,6 +1706,7 @@ struct EsMessageInstanceOpen {
|
||||||
struct EsMessageInstanceSave {
|
struct EsMessageInstanceSave {
|
||||||
ES_INSTANCE_TYPE *instance;
|
ES_INSTANCE_TYPE *instance;
|
||||||
EsFileStore *file;
|
EsFileStore *file;
|
||||||
|
STRING name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EsMessageInstanceDestroy {
|
struct EsMessageInstanceDestroy {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#ifdef ENABLE_POSIX_SUBSYSTEM
|
#ifdef ENABLE_POSIX_SUBSYSTEM
|
||||||
|
|
||||||
|
#define ARRAY_DEFINITIONS_ONLY
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
|
|
||||||
extern "C" void *ProcessorTLSRead(uintptr_t offset);
|
extern "C" void *ProcessorTLSRead(uintptr_t offset);
|
||||||
|
|
|
@ -3179,9 +3179,12 @@ void EsTextboxStartEdit(EsTextbox *textbox) {
|
||||||
|
|
||||||
void TextboxEndEdit(EsTextbox *textbox, bool reject) {
|
void TextboxEndEdit(EsTextbox *textbox, bool reject) {
|
||||||
if ((textbox->flags & ES_TEXTBOX_EDIT_BASED) && textbox->editing) {
|
if ((textbox->flags & ES_TEXTBOX_EDIT_BASED) && textbox->editing) {
|
||||||
|
TextboxSetActiveLine(textbox, -1);
|
||||||
textbox->editing = false;
|
textbox->editing = false;
|
||||||
EsMessage m = { ES_MSG_TEXTBOX_EDIT_END };
|
EsMessage m = { ES_MSG_TEXTBOX_EDIT_END };
|
||||||
m.endEdit.rejected = reject;
|
m.endEdit.rejected = reject;
|
||||||
|
m.endEdit.unchanged = textbox->dataBytes == textbox->editStartContentBytes
|
||||||
|
&& 0 == EsMemoryCompare(textbox->data, textbox->editStartContent, textbox->dataBytes);
|
||||||
|
|
||||||
if (reject || ES_REJECTED == EsMessageSend(textbox, &m)) {
|
if (reject || ES_REJECTED == EsMessageSend(textbox, &m)) {
|
||||||
EsTextboxSelectAll(textbox);
|
EsTextboxSelectAll(textbox);
|
||||||
|
|
|
@ -125,6 +125,7 @@ KSpinlock ipiLock;
|
||||||
#include <shared/heap.cpp>
|
#include <shared/heap.cpp>
|
||||||
#include <shared/arena.cpp>
|
#include <shared/arena.cpp>
|
||||||
#else
|
#else
|
||||||
|
#define ARRAY_IMPLEMENTATION_ONLY
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
#include <shared/partitions.cpp>
|
#include <shared/partitions.cpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -448,7 +448,9 @@ void *MMMapShared(MMSpace *space, MMSharedRegion *sharedRegion, uintptr_t offset
|
||||||
// Panics on failure.
|
// Panics on failure.
|
||||||
void MMCheckUnusable(uintptr_t physicalStart, size_t bytes);
|
void MMCheckUnusable(uintptr_t physicalStart, size_t bytes);
|
||||||
|
|
||||||
|
#define ARRAY_DEFINITIONS_ONLY
|
||||||
#include <shared/array.cpp>
|
#include <shared/array.cpp>
|
||||||
|
#undef ARRAY_DEFINITIONS_ONLY
|
||||||
|
|
||||||
typedef SimpleList MMObjectCacheItem;
|
typedef SimpleList MMObjectCacheItem;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef IMPLEMENTATION
|
#ifndef ARRAY_IMPLEMENTATION_ONLY
|
||||||
|
|
||||||
struct _ArrayHeader {
|
struct _ArrayHeader {
|
||||||
size_t length, allocated;
|
size_t length, allocated;
|
||||||
|
@ -71,7 +71,9 @@ struct Array {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARRAY_DEFINITIONS_ONLY
|
||||||
|
|
||||||
bool _ArrayMaybeInitialise(void **array, size_t itemSize, EsHeap *heap) {
|
bool _ArrayMaybeInitialise(void **array, size_t itemSize, EsHeap *heap) {
|
||||||
if (*array) return true;
|
if (*array) return true;
|
||||||
|
|
1290
shared/stb_image.h
1290
shared/stb_image.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -189,6 +189,9 @@ DEFINE_INTERFACE_STRING(ImageEditorPickTool, "Pick tool");
|
||||||
|
|
||||||
DEFINE_INTERFACE_STRING(ImageEditorUnsupportedFormat, "The image is in an unsupported format. Try opening it with another application.");
|
DEFINE_INTERFACE_STRING(ImageEditorUnsupportedFormat, "The image is in an unsupported format. Try opening it with another application.");
|
||||||
|
|
||||||
|
DEFINE_INTERFACE_STRING(ImageEditorNewFileName, "untitled.png");
|
||||||
|
DEFINE_INTERFACE_STRING(ImageEditorNewDocument, "New bitmap image");
|
||||||
|
|
||||||
DEFINE_INTERFACE_STRING(ImageEditorTitle, "Image Editor");
|
DEFINE_INTERFACE_STRING(ImageEditorTitle, "Image Editor");
|
||||||
|
|
||||||
// Text Editor.
|
// Text Editor.
|
||||||
|
|
|
@ -287,6 +287,7 @@ Option options[] = {
|
||||||
{ "Flag._ALWAYS_USE_VBE", OPTION_TYPE_BOOL, { .b = false } },
|
{ "Flag._ALWAYS_USE_VBE", OPTION_TYPE_BOOL, { .b = false } },
|
||||||
{ "Dependency.ACPICA", OPTION_TYPE_BOOL, { .b = true } },
|
{ "Dependency.ACPICA", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
{ "Dependency.stb_image", OPTION_TYPE_BOOL, { .b = true } },
|
{ "Dependency.stb_image", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
|
{ "Dependency.stb_image_write", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
{ "Dependency.stb_sprintf", OPTION_TYPE_BOOL, { .b = true } },
|
{ "Dependency.stb_sprintf", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
{ "Dependency.HarfBuzz", OPTION_TYPE_BOOL, { .b = true } },
|
{ "Dependency.HarfBuzz", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
{ "Dependency.FreeType", OPTION_TYPE_BOOL, { .b = true } },
|
{ "Dependency.FreeType", OPTION_TYPE_BOOL, { .b = true } },
|
||||||
|
|
|
@ -1266,6 +1266,8 @@ int main(int argc, char **argv) {
|
||||||
strcat(kernelCompileFlags, " -DUSE_ACPICA ");
|
strcat(kernelCompileFlags, " -DUSE_ACPICA ");
|
||||||
} else if (0 == strcmp(s.key, "Dependency.stb_image") && atoi(s.value)) {
|
} else if (0 == strcmp(s.key, "Dependency.stb_image") && atoi(s.value)) {
|
||||||
strcat(commonCompileFlags, " -DUSE_STB_IMAGE ");
|
strcat(commonCompileFlags, " -DUSE_STB_IMAGE ");
|
||||||
|
} else if (0 == strcmp(s.key, "Dependency.stb_image_write") && atoi(s.value)) {
|
||||||
|
strcat(commonCompileFlags, " -DUSE_STB_IMAGE_WRITE ");
|
||||||
} else if (0 == strcmp(s.key, "Dependency.stb_sprintf") && atoi(s.value)) {
|
} else if (0 == strcmp(s.key, "Dependency.stb_sprintf") && atoi(s.value)) {
|
||||||
strcat(commonCompileFlags, " -DUSE_STB_SPRINTF ");
|
strcat(commonCompileFlags, " -DUSE_STB_SPRINTF ");
|
||||||
} else if (0 == strcmp(s.key, "Dependency.HarfBuzz") && atoi(s.value)) {
|
} else if (0 == strcmp(s.key, "Dependency.HarfBuzz") && atoi(s.value)) {
|
||||||
|
|
Loading…
Reference in New Issue