mirror of https://gitlab.com/nakst/essence
improve tasks button
This commit is contained in:
parent
ecdce9e720
commit
6bdb99a79b
|
@ -164,7 +164,60 @@ void CommandCopy(Instance *instance, EsElement *, EsCommand *) {
|
||||||
CommandCopyOrCut(instance, ES_FLAGS_DEFAULT);
|
CommandCopyOrCut(instance, ES_FLAGS_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
EsError CommandPasteFile(String source, String destinationBase, void **copyBuffer, bool move, String *_destination) {
|
struct PasteOperation {
|
||||||
|
String source, destination;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PasteTask {
|
||||||
|
// Input:
|
||||||
|
String destinationBase;
|
||||||
|
bool move;
|
||||||
|
char *pathList;
|
||||||
|
size_t pathListBytes;
|
||||||
|
|
||||||
|
// State:
|
||||||
|
bool progressByData;
|
||||||
|
EsFileOffset totalDataToProcess, totalDataProcessed;
|
||||||
|
size_t sourceItemCount, sourceItemsProcessed;
|
||||||
|
EsUserTask *userTask;
|
||||||
|
EsFileOffset lastBytesCopied, cumulativeSecondBytesCopied;
|
||||||
|
EsFileOffsetDifference bytesPerSecond;
|
||||||
|
double cumulativeSecondTimeStampMs;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CommandPasteCopyCallback(EsFileOffset bytesCopied, EsFileOffset totalBytes, EsGeneric data) {
|
||||||
|
(void) totalBytes;
|
||||||
|
|
||||||
|
PasteTask *task = (PasteTask *) data.p;
|
||||||
|
EsFileOffset delta = bytesCopied - task->lastBytesCopied;
|
||||||
|
task->totalDataProcessed += delta;
|
||||||
|
task->lastBytesCopied = bytesCopied;
|
||||||
|
|
||||||
|
if (task->progressByData) {
|
||||||
|
double timeStampMs = EsTimeStampMs();
|
||||||
|
|
||||||
|
if (!task->cumulativeSecondTimeStampMs) {
|
||||||
|
task->cumulativeSecondTimeStampMs = timeStampMs;
|
||||||
|
task->bytesPerSecond = -1;
|
||||||
|
} else if (timeStampMs - task->cumulativeSecondTimeStampMs > 1000.0) {
|
||||||
|
// TODO Test that this calculation is correct.
|
||||||
|
task->bytesPerSecond = task->cumulativeSecondBytesCopied / (timeStampMs - task->cumulativeSecondTimeStampMs);
|
||||||
|
task->cumulativeSecondTimeStampMs = timeStampMs;
|
||||||
|
task->cumulativeSecondBytesCopied = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
task->cumulativeSecondBytesCopied += delta;
|
||||||
|
EsUserTaskSetProgress(task->userTask, (double) task->totalDataProcessed / task->totalDataToProcess, task->bytesPerSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EsUserTaskIsRunning(task->userTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
EsError CommandPasteFile(String source, String destinationBase, void **copyBuffer, PasteTask *task, String *_destination) {
|
||||||
|
if (!EsUserTaskIsRunning(task->userTask)) {
|
||||||
|
return ES_ERROR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
if (PathHasPrefix(destinationBase, source)) {
|
if (PathHasPrefix(destinationBase, source)) {
|
||||||
return ES_ERROR_TARGET_WITHIN_SOURCE;
|
return ES_ERROR_TARGET_WITHIN_SOURCE;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +227,7 @@ EsError CommandPasteFile(String source, String destinationBase, void **copyBuffe
|
||||||
EsError error;
|
EsError error;
|
||||||
|
|
||||||
if (StringEquals(PathGetParent(source), destinationBase)) {
|
if (StringEquals(PathGetParent(source), destinationBase)) {
|
||||||
if (move) {
|
if (task->move) {
|
||||||
// Move with the source and destination folders identical; meaningless.
|
// Move with the source and destination folders identical; meaningless.
|
||||||
error = ES_SUCCESS;
|
error = ES_SUCCESS;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -193,17 +246,19 @@ EsError CommandPasteFile(String source, String destinationBase, void **copyBuffe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EsPrint("%z %s -> %s...\n", move ? "Moving" : "Copying", STRFMT(source), STRFMT(destination));
|
EsPrint("%z %s -> %s...\n", task->move ? "Moving" : "Copying", STRFMT(source), STRFMT(destination));
|
||||||
|
|
||||||
if (move) {
|
if (task->move) {
|
||||||
error = EsPathMove(STRING(source), STRING(destination), ES_FLAGS_DEFAULT);
|
error = EsPathMove(STRING(source), STRING(destination), ES_FLAGS_DEFAULT);
|
||||||
|
|
||||||
if (error == ES_ERROR_VOLUME_MISMATCH) {
|
if (error == ES_ERROR_VOLUME_MISMATCH) {
|
||||||
// TODO Delete the files after all copies complete successfully.
|
// TODO Delete the files after all copies complete successfully.
|
||||||
error = EsFileCopy(STRING(source), STRING(destination), copyBuffer);
|
goto copy;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error = EsFileCopy(STRING(source), STRING(destination), copyBuffer);
|
copy:;
|
||||||
|
task->lastBytesCopied = 0;
|
||||||
|
error = EsFileCopy(STRING(source), STRING(destination), copyBuffer, CommandPasteCopyCallback, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == ES_ERROR_INCORRECT_NODE_TYPE) {
|
if (error == ES_ERROR_INCORRECT_NODE_TYPE) {
|
||||||
|
@ -219,7 +274,7 @@ EsError CommandPasteFile(String source, String destinationBase, void **copyBuffe
|
||||||
for (intptr_t i = 0; i < childCount && error == ES_SUCCESS; i++) {
|
for (intptr_t i = 0; i < childCount && error == ES_SUCCESS; i++) {
|
||||||
String childSourcePath = StringAllocateAndFormat("%s%z%s", STRFMT(source),
|
String childSourcePath = StringAllocateAndFormat("%s%z%s", STRFMT(source),
|
||||||
PathHasTrailingSlash(source) ? "" : "/", buffer[i].nameBytes, buffer[i].name);
|
PathHasTrailingSlash(source) ? "" : "/", buffer[i].nameBytes, buffer[i].name);
|
||||||
error = CommandPasteFile(childSourcePath, destination, copyBuffer, move, nullptr);
|
error = CommandPasteFile(childSourcePath, destination, copyBuffer, task, nullptr);
|
||||||
StringDestroy(&childSourcePath);
|
StringDestroy(&childSourcePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +285,7 @@ EsError CommandPasteFile(String source, String destinationBase, void **copyBuffe
|
||||||
|
|
||||||
if (error == ES_SUCCESS) {
|
if (error == ES_SUCCESS) {
|
||||||
EsMessageMutexAcquire();
|
EsMessageMutexAcquire();
|
||||||
if (move) FolderFileUpdatedAtPath(source, nullptr);
|
if (task->move) FolderFileUpdatedAtPath(source, nullptr);
|
||||||
FolderFileUpdatedAtPath(destination, nullptr);
|
FolderFileUpdatedAtPath(destination, nullptr);
|
||||||
EsMessageMutexRelease();
|
EsMessageMutexRelease();
|
||||||
}
|
}
|
||||||
|
@ -246,46 +301,75 @@ EsError CommandPasteFile(String source, String destinationBase, void **copyBuffe
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PasteOperation {
|
void CommandPasteTask(EsUserTask *userTask, EsGeneric _task) {
|
||||||
String source, destination;
|
// TODO Reporting errors properly. Ask to retry or skip.
|
||||||
};
|
// TODO If the destination file already exists, ask to rename or skip (as replace is destructive, it should be an advanced option).
|
||||||
|
|
||||||
struct PasteTask {
|
|
||||||
// Input:
|
|
||||||
String destinationBase;
|
|
||||||
bool move;
|
|
||||||
char *pathList;
|
|
||||||
size_t pathListBytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CommandPasteTask(EsGeneric _task) {
|
|
||||||
// TODO Background task.
|
|
||||||
// TODO Reporting errors properly. Ask to retry or cancel.
|
|
||||||
// TODO If the destination file already exists, ask to replace, skip, rename or cancel.
|
|
||||||
// TODO Other namespace handlers.
|
// TODO Other namespace handlers.
|
||||||
// TODO Undo.
|
// TODO Undo.
|
||||||
|
|
||||||
PasteTask *task = (PasteTask *) _task.p;
|
PasteTask *task = (PasteTask *) _task.p;
|
||||||
Array<PasteOperation> pasteOperations = {};
|
Array<PasteOperation> pasteOperations = {};
|
||||||
EsError error = ES_SUCCESS;
|
EsError error = ES_SUCCESS;
|
||||||
|
task->userTask = userTask;
|
||||||
|
|
||||||
void *copyBuffer = nullptr;
|
void *copyBuffer = nullptr;
|
||||||
const char *position = task->pathList;
|
|
||||||
|
|
||||||
while (task->pathListBytes) {
|
const char *position = task->pathList;
|
||||||
const char *newline = (const char *) EsCRTmemchr(position, '\n', task->pathListBytes);
|
size_t remainingBytes = task->pathListBytes;
|
||||||
|
|
||||||
|
while (remainingBytes && EsUserTaskIsRunning(task->userTask)) {
|
||||||
|
const char *newline = (const char *) EsCRTmemchr(position, '\n', remainingBytes);
|
||||||
|
if (!newline) break;
|
||||||
|
|
||||||
|
String source = StringFromLiteralWithSize(position, newline - position);
|
||||||
|
|
||||||
|
if (!task->move || !StringEquals(PathGetDrive(source), PathGetDrive(task->destinationBase))) {
|
||||||
|
// Files are actually being copied, so report progress by the amount of data copied,
|
||||||
|
// rather than the amount of files processed.
|
||||||
|
task->progressByData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EsDirectoryChild information;
|
||||||
|
|
||||||
|
if (EsPathQueryInformation(STRING(source), &information)) {
|
||||||
|
if (information.fileSize == -1) {
|
||||||
|
// TODO Support progress on volumes that don't report total directory sizes.
|
||||||
|
} else {
|
||||||
|
task->totalDataToProcess += information.fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
task->sourceItemCount++;
|
||||||
|
} else {
|
||||||
|
// We will probably error on this file, so ignore it.
|
||||||
|
}
|
||||||
|
|
||||||
|
position += source.bytes + 1;
|
||||||
|
remainingBytes -= source.bytes + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
position = task->pathList;
|
||||||
|
remainingBytes = task->pathListBytes;
|
||||||
|
|
||||||
|
while (remainingBytes && EsUserTaskIsRunning(task->userTask)) {
|
||||||
|
const char *newline = (const char *) EsCRTmemchr(position, '\n', remainingBytes);
|
||||||
if (!newline) break;
|
if (!newline) break;
|
||||||
|
|
||||||
String source = StringFromLiteralWithSize(position, newline - position);
|
String source = StringFromLiteralWithSize(position, newline - position);
|
||||||
String destination;
|
String destination;
|
||||||
error = CommandPasteFile(source, task->destinationBase, ©Buffer, task->move, &destination);
|
error = CommandPasteFile(source, task->destinationBase, ©Buffer, task, &destination);
|
||||||
if (error != ES_SUCCESS) break;
|
if (error != ES_SUCCESS) break;
|
||||||
|
|
||||||
PasteOperation operation = { .source = StringDuplicate(source), .destination = destination };
|
PasteOperation operation = { .source = StringDuplicate(source), .destination = destination };
|
||||||
pasteOperations.Add(operation);
|
pasteOperations.Add(operation);
|
||||||
|
|
||||||
position += source.bytes + 1;
|
position += source.bytes + 1;
|
||||||
task->pathListBytes -= source.bytes + 1;
|
remainingBytes -= source.bytes + 1;
|
||||||
|
|
||||||
|
task->sourceItemsProcessed++;
|
||||||
|
|
||||||
|
if (!task->progressByData) {
|
||||||
|
EsUserTaskSetProgress(userTask, (double) task->sourceItemsProcessed / task->sourceItemCount, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EsMessageMutexAcquire();
|
EsMessageMutexAcquire();
|
||||||
|
@ -357,7 +441,15 @@ void CommandPaste(Instance *instance, EsElement *, EsCommand *) {
|
||||||
task->destinationBase = StringDuplicate(instance->folder->path);
|
task->destinationBase = StringDuplicate(instance->folder->path);
|
||||||
instance->issuedPasteTask = task;
|
instance->issuedPasteTask = task;
|
||||||
|
|
||||||
if (ES_SUCCESS != EsUserTaskStart(CommandPasteTask, task)) {
|
EsError error;
|
||||||
|
|
||||||
|
if (task->move) {
|
||||||
|
error = EsUserTaskStart(CommandPasteTask, task, INTERFACE_STRING(FileManagerMoveTask), ES_ICON_FOLDER_MOVE);
|
||||||
|
} else {
|
||||||
|
error = EsUserTaskStart(CommandPasteTask, task, INTERFACE_STRING(FileManagerCopyTask), ES_ICON_FOLDER_COPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != ES_SUCCESS) {
|
||||||
EsPoint point = EsListViewGetAnnouncementPointForSelection(instance->list);
|
EsPoint point = EsListViewGetAnnouncementPointForSelection(instance->list);
|
||||||
EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, point.x, point.y, INTERFACE_STRING(CommonAnnouncementPasteErrorOther));
|
EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, point.x, point.y, INTERFACE_STRING(CommonAnnouncementPasteErrorOther));
|
||||||
EsHeapFree(task->pathList);
|
EsHeapFree(task->pathList);
|
||||||
|
|
|
@ -662,7 +662,7 @@ void ListItemCreated(EsElement *element, uintptr_t index, bool fromFolderRename)
|
||||||
|
|
||||||
thumbnail->generatingTasksInProgress++;
|
thumbnail->generatingTasksInProgress++;
|
||||||
|
|
||||||
String path = StringAllocateAndFormat("%s%s", STRFMT(instance->path), STRFMT(entry->GetName()));
|
String path = StringAllocateAndFormat("%s%s", STRFMT(instance->path), STRFMT(entry->GetInternalName()));
|
||||||
|
|
||||||
Task task = {
|
Task task = {
|
||||||
.string = path,
|
.string = path,
|
||||||
|
@ -805,6 +805,11 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
||||||
EsElement *element = message->createItem.item;
|
EsElement *element = message->createItem.item;
|
||||||
element->messageUser = ListItemMessage;
|
element->messageUser = ListItemMessage;
|
||||||
ListItemCreated(element, message->createItem.index, false);
|
ListItemCreated(element, message->createItem.index, false);
|
||||||
|
} else if (message->type == ES_MSG_LIST_VIEW_CONTEXT_MENU) {
|
||||||
|
EsMenu *menu = EsMenuCreate(element, ES_MENU_AT_CURSOR);
|
||||||
|
EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonClipboardCut), EsCommandByID(instance, ES_COMMAND_CUT));
|
||||||
|
EsMenuAddCommand(menu, ES_FLAGS_DEFAULT, INTERFACE_STRING(CommonClipboardCopy), EsCommandByID(instance, ES_COMMAND_COPY));
|
||||||
|
EsMenuShow(menu);
|
||||||
} else if (message->type == ES_MSG_MOUSE_RIGHT_CLICK) {
|
} else if (message->type == ES_MSG_MOUSE_RIGHT_CLICK) {
|
||||||
EsMenu *menu = EsMenuCreate(element, ES_MENU_AT_CURSOR);
|
EsMenu *menu = EsMenuCreate(element, ES_MENU_AT_CURSOR);
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct EnumString { const char *cName; int value; };
|
||||||
#define DESKTOP_MSG_FILE_TYPES_GET (14)
|
#define DESKTOP_MSG_FILE_TYPES_GET (14)
|
||||||
#define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15)
|
#define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15)
|
||||||
#define DESKTOP_MSG_START_USER_TASK (16)
|
#define DESKTOP_MSG_START_USER_TASK (16)
|
||||||
|
#define DESKTOP_MSG_SET_PROGRESS (17)
|
||||||
|
|
||||||
struct EsFileStore {
|
struct EsFileStore {
|
||||||
#define FILE_STORE_HANDLE (1)
|
#define FILE_STORE_HANDLE (1)
|
||||||
|
@ -350,6 +351,23 @@ int64_t EsSystemConfigurationReadInteger(const char *section, ptrdiff_t sectionB
|
||||||
return EsSystemConfigurationGroupReadInteger(group, key, keyBytes, defaultValue);
|
return EsSystemConfigurationGroupReadInteger(group, key, keyBytes, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemConfigurationUnload() {
|
||||||
|
for (uintptr_t i = 0; i < api.systemConfigurationGroups.Length(); i++) {
|
||||||
|
for (uintptr_t j = 0; j < api.systemConfigurationGroups[i].itemCount; j++) {
|
||||||
|
EsHeapFree(api.systemConfigurationGroups[i].items[j].key);
|
||||||
|
EsHeapFree(api.systemConfigurationGroups[i].items[j].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
EsHeapFree(api.systemConfigurationGroups[i].section);
|
||||||
|
EsHeapFree(api.systemConfigurationGroups[i].sectionClass);
|
||||||
|
|
||||||
|
Array<EsSystemConfigurationItem> items = { api.systemConfigurationGroups[i].items };
|
||||||
|
items.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
api.systemConfigurationGroups.Free();
|
||||||
|
}
|
||||||
|
|
||||||
void SystemConfigurationLoad(char *file, size_t fileBytes) {
|
void SystemConfigurationLoad(char *file, size_t fileBytes) {
|
||||||
EsINIState s = {};
|
EsINIState s = {};
|
||||||
s.buffer = file;
|
s.buffer = file;
|
||||||
|
@ -784,6 +802,16 @@ EsMessage *EsMessageReceive() {
|
||||||
EsAssert(message.message.instanceSave.file->operationComplete);
|
EsAssert(message.message.instanceSave.file->operationComplete);
|
||||||
FileStoreCloseHandle(message.message.instanceSave.file);
|
FileStoreCloseHandle(message.message.instanceSave.file);
|
||||||
} else if (message.message.type == ES_MSG_APPLICATION_EXIT) {
|
} else if (message.message.type == ES_MSG_APPLICATION_EXIT) {
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
|
GlyphCacheFree();
|
||||||
|
FreeUnusedStyles();
|
||||||
|
theming.loadedStyles.Free();
|
||||||
|
SystemConfigurationUnload();
|
||||||
|
api.mountPoints.Free();
|
||||||
|
api.postBox.Free();
|
||||||
|
api.timers.Free();
|
||||||
|
EsPrint("ES_MSG_APPLICATION_EXIT - Heap allocation count: %d.\n", heap.allocationsCount);
|
||||||
|
#endif
|
||||||
EsProcessTerminateCurrent();
|
EsProcessTerminateCurrent();
|
||||||
} else if (message.message.type == ES_MSG_INSTANCE_DESTROY) {
|
} else if (message.message.type == ES_MSG_INSTANCE_DESTROY) {
|
||||||
api.openInstanceCount--;
|
api.openInstanceCount--;
|
||||||
|
@ -1181,7 +1209,7 @@ extern "C" void _start(EsProcessStartupInformation *_startupInformation) {
|
||||||
uint8_t m = DESKTOP_MSG_SYSTEM_CONFIGURATION_GET;
|
uint8_t m = DESKTOP_MSG_SYSTEM_CONFIGURATION_GET;
|
||||||
EsBuffer responseBuffer = { .canGrow = true };
|
EsBuffer responseBuffer = { .canGrow = true };
|
||||||
MessageDesktop(&m, 1, ES_INVALID_HANDLE, &responseBuffer);
|
MessageDesktop(&m, 1, ES_INVALID_HANDLE, &responseBuffer);
|
||||||
SystemConfigurationLoad((char *) responseBuffer.in, responseBuffer.bytes);
|
SystemConfigurationLoad((char *) responseBuffer.out, responseBuffer.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiProcess) {
|
if (uiProcess) {
|
||||||
|
@ -1548,15 +1576,18 @@ const void *EsEmbeddedFileGet(const char *_name, ptrdiff_t nameBytes, size_t *by
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserTask {
|
struct EsUserTask {
|
||||||
EsUserTaskCallback callback;
|
EsUserTaskCallback callback;
|
||||||
EsGeneric data;
|
EsGeneric data;
|
||||||
EsHandle taskHandle;
|
EsHandle taskHandle;
|
||||||
|
|
||||||
|
#define USER_TASK_MINIMUM_TIME_BETWEEN_PROGRESS_MESSAGES_MS (50)
|
||||||
|
double lastProgressMs;
|
||||||
};
|
};
|
||||||
|
|
||||||
void UserTaskThread(EsGeneric _task) {
|
void UserTaskThread(EsGeneric _task) {
|
||||||
UserTask *task = (UserTask *) _task.p;
|
EsUserTask *task = (EsUserTask *) _task.p;
|
||||||
task->callback(task->data);
|
task->callback(task, task->data);
|
||||||
EsMessageMutexAcquire();
|
EsMessageMutexAcquire();
|
||||||
api.openInstanceCount--;
|
api.openInstanceCount--;
|
||||||
// TODO Send ES_MSG_APPLICATION_EXIT if needed.
|
// TODO Send ES_MSG_APPLICATION_EXIT if needed.
|
||||||
|
@ -1566,10 +1597,32 @@ void UserTaskThread(EsGeneric _task) {
|
||||||
EsHeapFree(task);
|
EsHeapFree(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
EsError EsUserTaskStart(EsUserTaskCallback callback, EsGeneric data) {
|
void EsUserTaskSetProgress(EsUserTask *task, double progress, EsFileOffsetDifference bytesPerSecond) {
|
||||||
|
(void) bytesPerSecond;
|
||||||
|
double timeMs = EsTimeStampMs();
|
||||||
|
|
||||||
|
if (timeMs - task->lastProgressMs >= USER_TASK_MINIMUM_TIME_BETWEEN_PROGRESS_MESSAGES_MS) {
|
||||||
|
task->lastProgressMs = timeMs;
|
||||||
|
progress = ClampDouble(0.0, 1.0, progress);
|
||||||
|
uint8_t buffer[1 + sizeof(double)];
|
||||||
|
buffer[0] = DESKTOP_MSG_SET_PROGRESS;
|
||||||
|
EsMemoryCopy(buffer + 1, &progress, sizeof(double));
|
||||||
|
MessageDesktop(buffer, sizeof(buffer), task->taskHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EsUserTaskIsRunning(EsUserTask *task) {
|
||||||
|
(void) task;
|
||||||
|
return true; // TODO.
|
||||||
|
}
|
||||||
|
|
||||||
|
EsError EsUserTaskStart(EsUserTaskCallback callback, EsGeneric data, const char *title, ptrdiff_t titleBytes, uint32_t iconID) {
|
||||||
|
// TODO Only tell the Desktop about the task if it's going to take >1 seconds.
|
||||||
|
// Maybe check after 500ms if the task is <50% complete?
|
||||||
|
|
||||||
EsMessageMutexCheck();
|
EsMessageMutexCheck();
|
||||||
|
|
||||||
UserTask *task = (UserTask *) EsHeapAllocate(sizeof(UserTask), true);
|
EsUserTask *task = (EsUserTask *) EsHeapAllocate(sizeof(EsUserTask), true);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return ES_ERROR_INSUFFICIENT_RESOURCES;
|
return ES_ERROR_INSUFFICIENT_RESOURCES;
|
||||||
|
@ -1587,6 +1640,15 @@ EsError EsUserTaskStart(EsUserTaskCallback callback, EsGeneric data) {
|
||||||
return ES_ERROR_INSUFFICIENT_RESOURCES;
|
return ES_ERROR_INSUFFICIENT_RESOURCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
buffer[0] = DESKTOP_MSG_SET_ICON;
|
||||||
|
EsMemoryCopy(buffer + 1, &iconID, sizeof(uint32_t));
|
||||||
|
MessageDesktop(buffer, 1 + sizeof(uint32_t), handle);
|
||||||
|
size_t bytes = EsStringFormat(buffer, 4096, "%c%s", DESKTOP_MSG_SET_TITLE, titleBytes == -1 ? EsCStringLength(title) : titleBytes, title);
|
||||||
|
MessageDesktop(buffer, bytes, handle);
|
||||||
|
}
|
||||||
|
|
||||||
task->callback = callback;
|
task->callback = callback;
|
||||||
task->data = data;
|
task->data = data;
|
||||||
task->taskHandle = handle;
|
task->taskHandle = handle;
|
||||||
|
|
|
@ -151,6 +151,7 @@ struct ApplicationInstance {
|
||||||
char title[128];
|
char title[128];
|
||||||
size_t titleBytes;
|
size_t titleBytes;
|
||||||
uint32_t iconID;
|
uint32_t iconID;
|
||||||
|
double progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EsStyle styleNewTabContent = {
|
const EsStyle styleNewTabContent = {
|
||||||
|
@ -195,6 +196,9 @@ struct {
|
||||||
ClipboardInformation clipboardInformation;
|
ClipboardInformation clipboardInformation;
|
||||||
|
|
||||||
bool configurationModified;
|
bool configurationModified;
|
||||||
|
|
||||||
|
Array<ApplicationInstance *> allOngoingUserTasks;
|
||||||
|
double totalUserTaskProgress;
|
||||||
} desktop;
|
} desktop;
|
||||||
|
|
||||||
int TaskBarButtonMessage(EsElement *element, EsMessage *message);
|
int TaskBarButtonMessage(EsElement *element, EsMessage *message);
|
||||||
|
@ -731,12 +735,53 @@ int TaskBarMessage(EsElement *element, EsMessage *message) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TaskBarTasksButtonMessage(EsElement *, EsMessage *message) {
|
void TaskBarTasksButtonUpdate() {
|
||||||
|
if (desktop.allOngoingUserTasks.Length()) {
|
||||||
|
if (EsElementIsHidden(desktop.tasksButton)) {
|
||||||
|
EsPanelStartMovementAnimation((EsPanel *) EsElementGetLayoutParent(desktop.tasksButton), 1.5f /* duration scale */);
|
||||||
|
EsElementStartTransition(desktop.tasksButton, ES_TRANSITION_FADE_IN, ES_ELEMENT_TRANSITION_ENTRANCE, 1.5f);
|
||||||
|
EsElementSetHidden(desktop.tasksButton, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Maybe grow button.
|
||||||
|
} else {
|
||||||
|
// TODO Maybe shrink or hide button.
|
||||||
|
// Currently, this always hides it, but I don't think this is a good behaviour.
|
||||||
|
|
||||||
|
if (!EsElementIsHidden(desktop.tasksButton)) {
|
||||||
|
EsPanelStartMovementAnimation((EsPanel *) EsElementGetLayoutParent(desktop.tasksButton), 1.5f /* duration scale */);
|
||||||
|
EsElementStartTransition(desktop.tasksButton, ES_TRANSITION_FADE_OUT, ES_ELEMENT_TRANSITION_HIDE_AFTER_COMPLETE, 1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EsElementRepaint(desktop.tasksButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TaskBarTasksButtonMessage(EsElement *element, EsMessage *message) {
|
||||||
if (message->type == ES_MSG_GET_WIDTH) {
|
if (message->type == ES_MSG_GET_WIDTH) {
|
||||||
message->measure.width = GetConstantNumber("taskBarTasksButtonWidth");
|
message->measure.width = GetConstantNumber("taskBarTasksButtonWidth");
|
||||||
return ES_HANDLED;
|
} else if (message->type == ES_MSG_PAINT) {
|
||||||
|
char title[256];
|
||||||
|
size_t titleBytes;
|
||||||
|
|
||||||
|
if (desktop.allOngoingUserTasks.Length() > 1) {
|
||||||
|
// TODO Localization.
|
||||||
|
titleBytes = EsStringFormat(title, sizeof(title), "%d tasks" ELLIPSIS, desktop.allOngoingUserTasks.Length());
|
||||||
|
} else if (desktop.allOngoingUserTasks.Length() == 1) {
|
||||||
|
titleBytes = EsStringFormat(title, sizeof(title), "%s", desktop.allOngoingUserTasks.First()->titleBytes, desktop.allOngoingUserTasks.First()->title);
|
||||||
|
} else {
|
||||||
|
titleBytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EsDrawContent(message->painter, element, ES_RECT_2S(message->painter->width, message->painter->height), title, titleBytes);
|
||||||
} else if (message->type == ES_MSG_PAINT_ICON) {
|
} else if (message->type == ES_MSG_PAINT_ICON) {
|
||||||
float progress = 0.0f; // TODO.
|
double progress = !desktop.allOngoingUserTasks.Length() ? 1.0 : desktop.totalUserTaskProgress / desktop.allOngoingUserTasks.Length();
|
||||||
|
|
||||||
|
if (progress > 0.0 && progress <= 0.05) {
|
||||||
|
progress = 0.05; // Really small angles look strange; avoid them.
|
||||||
|
} else if (progress >= 0.9 && progress < 1.0) {
|
||||||
|
progress = (progress - 0.9) * 0.5 + 0.9; // Approach 95% from 90% at half speed to account for bad progress calculations.
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t color1 = GetConstantNumber("taskBarTasksButtonWheelColor1");
|
uint32_t color1 = GetConstantNumber("taskBarTasksButtonWheelColor1");
|
||||||
uint32_t color2 = GetConstantNumber("taskBarTasksButtonWheelColor2");
|
uint32_t color2 = GetConstantNumber("taskBarTasksButtonWheelColor2");
|
||||||
|
@ -784,10 +829,11 @@ int TaskBarTasksButtonMessage(EsElement *, EsMessage *message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RastSurfaceDestroy(&surface);
|
RastSurfaceDestroy(&surface);
|
||||||
return ES_HANDLED;
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ES_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownModalCreate() {
|
void ShutdownModalCreate() {
|
||||||
|
@ -1888,7 +1934,7 @@ void DesktopSetup() {
|
||||||
desktop.taskBar.taskList.Initialise(panel, ES_CELL_FILL, ReorderListMessage, nullptr);
|
desktop.taskBar.taskList.Initialise(panel, ES_CELL_FILL, ReorderListMessage, nullptr);
|
||||||
desktop.taskBar.taskList.cName = "task list";
|
desktop.taskBar.taskList.cName = "task list";
|
||||||
|
|
||||||
desktop.tasksButton = EsButtonCreate(panel, ES_ELEMENT_HIDDEN, ES_STYLE_TASK_BAR_BUTTON, "Copying files" ELLIPSIS, -1);
|
desktop.tasksButton = EsButtonCreate(panel, ES_ELEMENT_HIDDEN, ES_STYLE_TASK_BAR_BUTTON);
|
||||||
desktop.tasksButton->messageUser = TaskBarTasksButtonMessage;
|
desktop.tasksButton->messageUser = TaskBarTasksButtonMessage;
|
||||||
|
|
||||||
EsButton *shutdownButton = EsButtonCreate(panel, ES_FLAGS_DEFAULT, ES_STYLE_TASK_BAR_EXTRA);
|
EsButton *shutdownButton = EsButtonCreate(panel, ES_FLAGS_DEFAULT, ES_STYLE_TASK_BAR_EXTRA);
|
||||||
|
@ -2095,15 +2141,13 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
EsHandle targetWindowHandle = EsSyscall(ES_SYSCALL_WINDOW_SET_PROPERTY, windowHandle, processHandle, 0, ES_WINDOW_PROPERTY_EMBED_OWNER);
|
EsHandle targetWindowHandle = EsSyscall(ES_SYSCALL_WINDOW_SET_PROPERTY, windowHandle, processHandle, 0, ES_WINDOW_PROPERTY_EMBED_OWNER);
|
||||||
EsBufferWrite(pipe, &targetWindowHandle, sizeof(targetWindowHandle));
|
EsBufferWrite(pipe, &targetWindowHandle, sizeof(targetWindowHandle));
|
||||||
|
|
||||||
if (EsElementIsHidden(desktop.tasksButton)) {
|
desktop.allOngoingUserTasks.Add(instance);
|
||||||
EsPanelStartMovementAnimation((EsPanel *) EsElementGetLayoutParent(desktop.tasksButton), 1.5f /* duration scale */);
|
TaskBarTasksButtonUpdate();
|
||||||
EsElementStartTransition(desktop.tasksButton, ES_TRANSITION_FADE_IN, ES_ELEMENT_TRANSITION_ENTRANCE, 1.5f);
|
|
||||||
EsElementSetHidden(desktop.tasksButton, false);
|
|
||||||
}
|
|
||||||
} else if (!instance) {
|
} else if (!instance) {
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// | Messages below here require a valid instance. |
|
// | Messages below here require a valid instance. |
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
EsPrint("DesktopSyscall - Received message %d without an instance.\n", buffer[0]);
|
||||||
} else if (buffer[0] == DESKTOP_MSG_SET_TITLE || buffer[0] == DESKTOP_MSG_SET_ICON) {
|
} else if (buffer[0] == DESKTOP_MSG_SET_TITLE || buffer[0] == DESKTOP_MSG_SET_ICON) {
|
||||||
if (buffer[0] == DESKTOP_MSG_SET_TITLE) {
|
if (buffer[0] == DESKTOP_MSG_SET_TITLE) {
|
||||||
instance->titleBytes = EsStringFormat(instance->title, sizeof(instance->title), "%s",
|
instance->titleBytes = EsStringFormat(instance->title, sizeof(instance->title), "%s",
|
||||||
|
@ -2121,6 +2165,15 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
instance->tab->container->taskBarButton->Repaint(true);
|
instance->tab->container->taskBarButton->Repaint(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (buffer[0] == DESKTOP_MSG_SET_PROGRESS && message->desktop.bytes == 1 + sizeof(double) && instance->isUserTask) {
|
||||||
|
double progress;
|
||||||
|
EsMemoryCopy(&progress, buffer + 1, sizeof(double));
|
||||||
|
|
||||||
|
if (progress >= 0.0 && progress <= 1.0) {
|
||||||
|
desktop.totalUserTaskProgress += progress - instance->progress;
|
||||||
|
instance->progress = progress;
|
||||||
|
EsElementRepaint(desktop.tasksButton);
|
||||||
|
}
|
||||||
} else if (buffer[0] == DESKTOP_MSG_REQUEST_SAVE) {
|
} else if (buffer[0] == DESKTOP_MSG_REQUEST_SAVE) {
|
||||||
ApplicationInstanceRequestSave(instance, (const char *) buffer + 1, message->desktop.bytes - 1);
|
ApplicationInstanceRequestSave(instance, (const char *) buffer + 1, message->desktop.bytes - 1);
|
||||||
} else if (buffer[0] == DESKTOP_MSG_COMPLETE_SAVE) {
|
} else if (buffer[0] == DESKTOP_MSG_COMPLETE_SAVE) {
|
||||||
|
@ -2163,6 +2216,8 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
||||||
if (message.message.type != ES_MSG_INVALID) {
|
if (message.message.type != ES_MSG_INVALID) {
|
||||||
UIProcessWindowManagerMessage((EsWindow *) message.object, &message.message, nullptr);
|
UIProcessWindowManagerMessage((EsWindow *) message.object, &message.message, nullptr);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
EsPrint("DesktopSyscall - Received unhandled message %d.\n", buffer[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2218,6 +2273,11 @@ void EmbeddedWindowDestroyed(EsObjectID id) {
|
||||||
|
|
||||||
EsElementDestroy(instance->tab);
|
EsElementDestroy(instance->tab);
|
||||||
}
|
}
|
||||||
|
} else if (instance->isUserTask) {
|
||||||
|
desktop.totalUserTaskProgress -= instance->progress;
|
||||||
|
EsElementRepaint(desktop.tasksButton);
|
||||||
|
desktop.allOngoingUserTasks.FindAndDeleteSwap(instance, false /* ignore if not found */);
|
||||||
|
TaskBarTasksButtonUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
EsHeapFree(instance);
|
EsHeapFree(instance);
|
||||||
|
|
|
@ -125,7 +125,7 @@ struct EsElement : EsElementPublic {
|
||||||
Array<EsElement *> children;
|
Array<EsElement *> children;
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
|
|
||||||
uint8_t transitionType;
|
uint8_t transitionType, transitionFlags;
|
||||||
uint16_t customStyleState; // ORed to the style state in RefreshStyle.
|
uint16_t customStyleState; // ORed to the style state in RefreshStyle.
|
||||||
uint16_t previousStyleState; // Set by RefreshStyleState.
|
uint16_t previousStyleState; // Set by RefreshStyleState.
|
||||||
uint16_t transitionDurationMs, transitionTimeMs;
|
uint16_t transitionDurationMs, transitionTimeMs;
|
||||||
|
@ -337,6 +337,7 @@ struct EsPanel : EsElement {
|
||||||
EsPanelBand *bands[2];
|
EsPanelBand *bands[2];
|
||||||
uintptr_t tableIndex;
|
uintptr_t tableIndex;
|
||||||
|
|
||||||
|
// TODO This names overlap with fields in EsElement, they should probably be renamed.
|
||||||
uint16_t transitionType;
|
uint16_t transitionType;
|
||||||
uint32_t transitionTimeMs,
|
uint32_t transitionTimeMs,
|
||||||
transitionLengthMs;
|
transitionLengthMs;
|
||||||
|
@ -1308,6 +1309,10 @@ EsRectangle UIGetTransitionEffectRectangle(EsRectangle bounds, EsTransitionType
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIDrawTransitionEffect(EsPainter *painter, EsPaintTarget *sourceSurface, EsRectangle bounds, EsTransitionType type, double progress, bool to) {
|
void UIDrawTransitionEffect(EsPainter *painter, EsPaintTarget *sourceSurface, EsRectangle bounds, EsTransitionType type, double progress, bool to) {
|
||||||
|
if (type == ES_TRANSITION_FADE_OUT && to) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EsRectangle destinationRegion = UIGetTransitionEffectRectangle(bounds, type, progress, to);
|
EsRectangle destinationRegion = UIGetTransitionEffectRectangle(bounds, type, progress, to);
|
||||||
EsRectangle sourceRegion = ES_RECT_4(0, bounds.r - bounds.l, 0, bounds.b - bounds.t);
|
EsRectangle sourceRegion = ES_RECT_4(0, bounds.r - bounds.l, 0, bounds.b - bounds.t);
|
||||||
uint16_t alpha = (to ? progress : (1 - progress)) * 255;
|
uint16_t alpha = (to ? progress : (1 - progress)) * 255;
|
||||||
|
@ -1344,6 +1349,7 @@ void EsElementStartTransition(EsElement *element, EsTransitionType transitionTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
element->transitionTimeMs = 0;
|
element->transitionTimeMs = 0;
|
||||||
|
element->transitionFlags = flags;
|
||||||
element->transitionDurationMs = durationMs;
|
element->transitionDurationMs = durationMs;
|
||||||
element->transitionType = transitionType;
|
element->transitionType = transitionType;
|
||||||
element->StartAnimating();
|
element->StartAnimating();
|
||||||
|
@ -1673,6 +1679,10 @@ void ProcessAnimations() {
|
||||||
|
|
||||||
if (!transitionComplete) {
|
if (!transitionComplete) {
|
||||||
element->Repaint(true, ES_RECT_1(0));
|
element->Repaint(true, ES_RECT_1(0));
|
||||||
|
} else {
|
||||||
|
if (element->transitionFlags & ES_ELEMENT_TRANSITION_HIDE_AFTER_COMPLETE) {
|
||||||
|
EsElementSetHidden(element, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool backgroundAnimationComplete = ThemeAnimationComplete(&element->animation);
|
bool backgroundAnimationComplete = ThemeAnimationComplete(&element->animation);
|
||||||
|
@ -6248,7 +6258,10 @@ void UIHandleKeyMessage(EsWindow *window, EsMessage *message) {
|
||||||
|
|
||||||
if (window->focused) {
|
if (window->focused) {
|
||||||
message->type = ES_MSG_KEY_TYPED;
|
message->type = ES_MSG_KEY_TYPED;
|
||||||
if (EsMessageSend(window->focused, message) != 0) return;
|
|
||||||
|
if (EsMessageSend(window->focused, message) == ES_HANDLED /* allow messageUser to reject input */) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EsElement *element = window->focused;
|
EsElement *element = window->focused;
|
||||||
message->type = ES_MSG_KEY_DOWN;
|
message->type = ES_MSG_KEY_DOWN;
|
||||||
|
|
|
@ -19,6 +19,7 @@ opaque_type EsPaintTarget none;
|
||||||
opaque_type EsUndoManager none;
|
opaque_type EsUndoManager none;
|
||||||
opaque_type EsHeap none;
|
opaque_type EsHeap none;
|
||||||
opaque_type EsFileStore none;
|
opaque_type EsFileStore none;
|
||||||
|
opaque_type EsUserTask none;
|
||||||
|
|
||||||
type_name uint8_t EsNodeType;
|
type_name uint8_t EsNodeType;
|
||||||
type_name intptr_t EsError;
|
type_name intptr_t EsError;
|
||||||
|
@ -326,6 +327,7 @@ define ES_ERROR_CONNECTION_REFUSED (-68)
|
||||||
define ES_ERROR_ILLEGAL_PATH (-69)
|
define ES_ERROR_ILLEGAL_PATH (-69)
|
||||||
define ES_ERROR_NODE_NOT_LOADED (-71)
|
define ES_ERROR_NODE_NOT_LOADED (-71)
|
||||||
define ES_ERROR_DIRECTORY_ENTRY_BEING_REMOVED (-72)
|
define ES_ERROR_DIRECTORY_ENTRY_BEING_REMOVED (-72)
|
||||||
|
define ES_ERROR_CANCELLED (-73)
|
||||||
|
|
||||||
define ES_SYSTEM_CONSTANT_TIME_STAMP_UNITS_PER_MICROSECOND (0)
|
define ES_SYSTEM_CONSTANT_TIME_STAMP_UNITS_PER_MICROSECOND (0)
|
||||||
define ES_SYSTEM_CONSTANT_OPTIMAL_WORK_QUEUE_THREAD_COUNT (1)
|
define ES_SYSTEM_CONSTANT_OPTIMAL_WORK_QUEUE_THREAD_COUNT (1)
|
||||||
|
@ -666,6 +668,7 @@ define ES_MEMORY_RESERVE_COMMIT_ALL (1 << 0)
|
||||||
define ES_PANEL_SWITCHER_DESTROY_PREVIOUS_AFTER_TRANSITION (1 << 0)
|
define ES_PANEL_SWITCHER_DESTROY_PREVIOUS_AFTER_TRANSITION (1 << 0)
|
||||||
|
|
||||||
define ES_ELEMENT_TRANSITION_ENTRANCE (1 << 0)
|
define ES_ELEMENT_TRANSITION_ENTRANCE (1 << 0)
|
||||||
|
define ES_ELEMENT_TRANSITION_HIDE_AFTER_COMPLETE (1 << 1)
|
||||||
|
|
||||||
define ES_TEXT_GET_CHARACTER_AT_POINT_MIDDLE (1 << 0)
|
define ES_TEXT_GET_CHARACTER_AT_POINT_MIDDLE (1 << 0)
|
||||||
|
|
||||||
|
@ -910,7 +913,7 @@ enum EsMessageType {
|
||||||
// It will be removed from the `children` later (but before the next ES_MSG_LAYOUT message is received).
|
// It will be removed from the `children` later (but before the next ES_MSG_LAYOUT message is received).
|
||||||
ES_MSG_PRE_ADD_CHILD = 0x200D // An element has been created with this element as its parent, but is not yet added to the parent.
|
ES_MSG_PRE_ADD_CHILD = 0x200D // An element has been created with this element as its parent, but is not yet added to the parent.
|
||||||
ES_MSG_HIT_TEST = 0x200E // For non-rectangular elements: test whether a pixel should be considered inside the element. Set response to ES_HANDLED.
|
ES_MSG_HIT_TEST = 0x200E // For non-rectangular elements: test whether a pixel should be considered inside the element. Set response to ES_HANDLED.
|
||||||
ES_MSG_KEY_TYPED = 0x2011 // Sent to the focused element when a key is pressed.
|
ES_MSG_KEY_TYPED = 0x2011 // Sent to the focused element when a key is pressed. Only if ES_HANDLED is returned the message will not propagate; this allows messageUser to block input processing by returning ES_REJECTED.
|
||||||
ES_MSG_SCROLL_X = 0x2012 // The element has been horizontally scrolled.
|
ES_MSG_SCROLL_X = 0x2012 // The element has been horizontally scrolled.
|
||||||
ES_MSG_SCROLL_Y = 0x2013 // The element has been vertically scrolled.
|
ES_MSG_SCROLL_Y = 0x2013 // The element has been vertically scrolled.
|
||||||
ES_MSG_STRONG_FOCUS_END = 0x2014 // Sent once when the user 'clicks off' the element, even if a new element was not necessarily focused.
|
ES_MSG_STRONG_FOCUS_END = 0x2014 // Sent once when the user 'clicks off' the element, even if a new element was not necessarily focused.
|
||||||
|
@ -1887,7 +1890,8 @@ function_pointer void EsUndoCallback(const void *item, EsUndoManager *manager, E
|
||||||
function_pointer void EsMountPointEnumerationCallback(const char *prefix, size_t prefixBytes, EsGeneric context);
|
function_pointer void EsMountPointEnumerationCallback(const char *prefix, size_t prefixBytes, EsGeneric context);
|
||||||
function_pointer void EsListViewEnumerateVisibleItemsCallback(EsListView *view, EsElement *item, uint32_t group, EsGeneric index);
|
function_pointer void EsListViewEnumerateVisibleItemsCallback(EsListView *view, EsElement *item, uint32_t group, EsGeneric index);
|
||||||
function_pointer void EsFontEnumerationCallback(const EsFontInformation *information, EsGeneric context);
|
function_pointer void EsFontEnumerationCallback(const EsFontInformation *information, EsGeneric context);
|
||||||
function_pointer void EsUserTaskCallback(EsGeneric data);
|
function_pointer void EsUserTaskCallback(EsUserTask *task, EsGeneric data);
|
||||||
|
function_pointer bool EsFileCopyCallback(EsFileOffset bytesCopied, EsFileOffset totalBytes, EsGeneric data); // Return false to cancel.
|
||||||
|
|
||||||
// System.
|
// System.
|
||||||
|
|
||||||
|
@ -1933,7 +1937,7 @@ function EsError EsFileWriteAllFromHandle(EsHandle handle, const void *data, siz
|
||||||
function EsError EsFileWriteAllGather(STRING filePath, const void **data, size_t *fileSize, size_t gatherCount);
|
function EsError EsFileWriteAllGather(STRING filePath, const void **data, size_t *fileSize, size_t gatherCount);
|
||||||
function EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **data, size_t *fileSize, size_t gatherCount);
|
function EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **data, size_t *fileSize, size_t gatherCount);
|
||||||
function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags);
|
function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags);
|
||||||
function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = nullptr); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
|
function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = ES_NULL, EsFileCopyCallback callback = ES_NULL, EsGeneric data = ES_NULL); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
|
||||||
|
|
||||||
function EsError EsFileControl(EsHandle file, uint32_t flags);
|
function EsError EsFileControl(EsHandle file, uint32_t flags);
|
||||||
function EsFileInformation EsFileOpen(STRING path, uint32_t flags);
|
function EsFileInformation EsFileOpen(STRING path, uint32_t flags);
|
||||||
|
@ -2233,7 +2237,7 @@ function int EsCRTvsnprintf(char *buffer, size_t bufferSize, const char *format,
|
||||||
function EsError EsClipboardAddText(EsClipboard clipboard, STRING text = BLANK_STRING);
|
function EsError EsClipboardAddText(EsClipboard clipboard, STRING text = BLANK_STRING);
|
||||||
function bool EsClipboardHasFormat(EsClipboard clipboard, EsClipboardFormat format);
|
function bool EsClipboardHasFormat(EsClipboard clipboard, EsClipboardFormat format);
|
||||||
function bool EsClipboardHasData(EsClipboard clipboard);
|
function bool EsClipboardHasData(EsClipboard clipboard);
|
||||||
function char *EsClipboardReadText(EsClipboard clipboard, size_t *bytes, uint32_t *flags = nullptr); // Free with EsHeapFree.
|
function char *EsClipboardReadText(EsClipboard clipboard, size_t *bytes, uint32_t *flags = ES_NULL); // Free with EsHeapFree.
|
||||||
function EsFileStore *EsClipboardOpen(EsClipboard clipboard); // Open the clipboard for writing.
|
function EsFileStore *EsClipboardOpen(EsClipboard clipboard); // Open the clipboard for writing.
|
||||||
function EsError EsClipboardCloseAndAdd(EsClipboard clipboard, EsClipboardFormat format, EsFileStore *fileStore, uint32_t flags = ES_FLAGS_DEFAULT);
|
function EsError EsClipboardCloseAndAdd(EsClipboard clipboard, EsClipboardFormat format, EsFileStore *fileStore, uint32_t flags = ES_FLAGS_DEFAULT);
|
||||||
|
|
||||||
|
@ -2265,7 +2269,9 @@ function const EsApplicationStartupInformation *EsInstanceGetStartupInformation(
|
||||||
function void EsInstanceOpenComplete(EsMessage *message, bool success, STRING errorText = BLANK_STRING);
|
function void EsInstanceOpenComplete(EsMessage *message, bool success, STRING errorText = BLANK_STRING);
|
||||||
function void EsInstanceSaveComplete(EsMessage *message, bool success);
|
function void EsInstanceSaveComplete(EsMessage *message, bool success);
|
||||||
|
|
||||||
function EsError EsUserTaskStart(EsUserTaskCallback callback, EsGeneric data);
|
function EsError EsUserTaskStart(EsUserTaskCallback callback, EsGeneric data, STRING title, uint32_t iconID);
|
||||||
|
function void EsUserTaskSetProgress(EsUserTask *task, double progress, EsFileOffsetDifference bytesPerSecond); // Set bytesPerSecond to -1 if not applicable.
|
||||||
|
function bool EsUserTaskIsRunning(EsUserTask *task); // Returns false if the task was cancelled.
|
||||||
|
|
||||||
// Message processing.
|
// Message processing.
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,8 @@ void *EsFileReadAll(const char *filePath, ptrdiff_t filePathLength, size_t *file
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destination, ptrdiff_t destinationBytes, void **_copyBuffer) {
|
EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destination, ptrdiff_t destinationBytes, void **_copyBuffer,
|
||||||
|
EsFileCopyCallback callback, EsGeneric callbackData) {
|
||||||
const size_t copyBufferBytes = 262144;
|
const size_t copyBufferBytes = 262144;
|
||||||
void *copyBuffer = _copyBuffer && *_copyBuffer ? *_copyBuffer : EsHeapAllocate(copyBufferBytes, false);
|
void *copyBuffer = _copyBuffer && *_copyBuffer ? *_copyBuffer : EsHeapAllocate(copyBufferBytes, false);
|
||||||
if (_copyBuffer) *_copyBuffer = copyBuffer;
|
if (_copyBuffer) *_copyBuffer = copyBuffer;
|
||||||
|
@ -245,9 +246,25 @@ EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destin
|
||||||
if (error == ES_SUCCESS) {
|
if (error == ES_SUCCESS) {
|
||||||
for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) {
|
for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) {
|
||||||
size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer);
|
size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer);
|
||||||
if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; }
|
|
||||||
|
if (ES_CHECK_ERROR(bytesRead)) {
|
||||||
|
error = bytesRead;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer);
|
size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer);
|
||||||
if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; }
|
|
||||||
|
if (ES_CHECK_ERROR(bytesWritten)) {
|
||||||
|
error = bytesWritten;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
EsAssert(bytesRead == bytesWritten);
|
||||||
|
|
||||||
|
if (callback && !callback(i + bytesWritten, sourceFile.size, callbackData)) {
|
||||||
|
error = ES_ERROR_CANCELLED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,22 +95,25 @@ Font FontGet(EsFont key);
|
||||||
|
|
||||||
// --------------------------------- Glyph cache.
|
// --------------------------------- Glyph cache.
|
||||||
|
|
||||||
|
void GlyphCacheFreeEntry() {
|
||||||
|
GlyphCacheEntry *entry = fontManagement.glyphCacheLRU.lastItem->thisItem;
|
||||||
|
fontManagement.glyphCacheLRU.Remove(&entry->itemLRU);
|
||||||
|
fontManagement.glyphCache.Delete(&entry->key);
|
||||||
|
EsAssert(fontManagement.glyphCacheBytes >= entry->dataBytes);
|
||||||
|
fontManagement.glyphCacheBytes -= entry->dataBytes;
|
||||||
|
EsHeapFree(entry->data);
|
||||||
|
EsHeapFree(entry);
|
||||||
|
}
|
||||||
|
|
||||||
void RegisterGlyphCacheEntry(GlyphCacheKey key, GlyphCacheEntry *entry) {
|
void RegisterGlyphCacheEntry(GlyphCacheKey key, GlyphCacheEntry *entry) {
|
||||||
entry->itemLRU.thisItem = entry;
|
entry->itemLRU.thisItem = entry;
|
||||||
entry->key = key;
|
entry->key = key;
|
||||||
*fontManagement.glyphCache.Put(&key) = entry;
|
*fontManagement.glyphCache.Put(&key) = entry;
|
||||||
fontManagement.glyphCacheLRU.InsertStart(&entry->itemLRU);
|
fontManagement.glyphCacheLRU.InsertStart(&entry->itemLRU);
|
||||||
|
|
||||||
fontManagement.glyphCacheBytes += entry->dataBytes;
|
fontManagement.glyphCacheBytes += entry->dataBytes;
|
||||||
|
|
||||||
while (fontManagement.glyphCacheBytes > GLYPH_CACHE_MAX_SIZE) {
|
while (fontManagement.glyphCacheBytes > GLYPH_CACHE_MAX_SIZE) {
|
||||||
GlyphCacheEntry *leastRecentlyUsedGlyph = fontManagement.glyphCacheLRU.lastItem->thisItem;
|
GlyphCacheFreeEntry();
|
||||||
fontManagement.glyphCacheLRU.Remove(&leastRecentlyUsedGlyph->itemLRU);
|
|
||||||
fontManagement.glyphCache.Delete(&leastRecentlyUsedGlyph->key);
|
|
||||||
EsAssert(fontManagement.glyphCacheBytes >= entry->dataBytes); // Negative glyph cache bytes.
|
|
||||||
fontManagement.glyphCacheBytes -= entry->dataBytes;
|
|
||||||
EsHeapFree(leastRecentlyUsedGlyph->data);
|
|
||||||
EsHeapFree(leastRecentlyUsedGlyph);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +129,15 @@ GlyphCacheEntry *LookupGlyphCacheEntry(GlyphCacheKey key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlyphCacheFree() {
|
||||||
|
while (fontManagement.glyphCacheLRU.count) {
|
||||||
|
GlyphCacheFreeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
EsAssert(fontManagement.glyphCache.Count() == 0);
|
||||||
|
fontManagement.glyphCache.Free();
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------- Font renderer.
|
// --------------------------------- Font renderer.
|
||||||
|
|
||||||
bool FontLoad(Font *font, const void *data, size_t dataBytes) {
|
bool FontLoad(Font *font, const void *data, size_t dataBytes) {
|
||||||
|
|
|
@ -260,6 +260,8 @@ DEFINE_INTERFACE_STRING(FileManagerInvalidPath, "The current path does not lead
|
||||||
DEFINE_INTERFACE_STRING(FileManagerInvalidDrive, "The drive containing this folder was disconnected.");
|
DEFINE_INTERFACE_STRING(FileManagerInvalidDrive, "The drive containing this folder was disconnected.");
|
||||||
DEFINE_INTERFACE_STRING(FileManagerRefresh, "Refresh");
|
DEFINE_INTERFACE_STRING(FileManagerRefresh, "Refresh");
|
||||||
DEFINE_INTERFACE_STRING(FileManagerListContextActions, "Actions");
|
DEFINE_INTERFACE_STRING(FileManagerListContextActions, "Actions");
|
||||||
|
DEFINE_INTERFACE_STRING(FileManagerCopyTask, "Copying" ELLIPSIS);
|
||||||
|
DEFINE_INTERFACE_STRING(FileManagerMoveTask, "Moving" ELLIPSIS);
|
||||||
|
|
||||||
// TODO System Monitor.
|
// TODO System Monitor.
|
||||||
|
|
||||||
|
|
|
@ -434,3 +434,5 @@ EsListViewSelectNone=432
|
||||||
EsElementIsFocused=433
|
EsElementIsFocused=433
|
||||||
EsUserTaskStart=434
|
EsUserTaskStart=434
|
||||||
EsElementIsHidden=435
|
EsElementIsHidden=435
|
||||||
|
EsUserTaskSetProgress=436
|
||||||
|
EsUserTaskIsRunning=437
|
||||||
|
|
Loading…
Reference in New Issue