diff --git a/apps/file_manager/ui.cpp b/apps/file_manager/ui.cpp index 39201db..8447ee0 100644 --- a/apps/file_manager/ui.cpp +++ b/apps/file_manager/ui.cpp @@ -104,23 +104,34 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i EsCommandSetEnabled(&instance->commandRefresh, true); // Load the view settings for the folder. + // If the folder does not have any settings, inherit from closest ancestor with settings. bool foundViewSettings = false; + size_t lastMatchBytes = 0; + ptrdiff_t updateLRU = -1; if (folder->path.bytes < sizeof(folderViewSettings[0].path)) { for (uintptr_t i = 0; i < folderViewSettings.Length(); i++) { - if (folderViewSettings[i].pathBytes == folder->path.bytes - && 0 == EsMemoryCompare(folderViewSettings[i].path, STRING(folder->path))) { + String path = StringFromLiteralWithSize(folderViewSettings[i].path, folderViewSettings[i].pathBytes); + bool matchFull = StringEquals(path, folder->path); + bool matchPartial = matchFull || PathHasPrefix(folder->path, path); + + if (matchFull || (matchPartial && lastMatchBytes < path.bytes)) { foundViewSettings = true; - FolderViewSettingsEntry entry = folderViewSettings[i]; - instance->viewSettings = entry.settings; - folderViewSettings.Delete(i); - folderViewSettings.Add(entry); // Update the LRU order. - break; + instance->viewSettings = folderViewSettings[i].settings; + updateLRU = i; + if (matchFull) break; + else lastMatchBytes = path.bytes; // Keep looking for a closer ancestor. } } } + if (updateLRU != -1) { + FolderViewSettingsEntry entry = folderViewSettings[updateLRU]; + folderViewSettings.Delete(updateLRU); + folderViewSettings.Add(entry); + } + if (!foundViewSettings) { if (folder->itemHandler->getDefaultViewSettings) { folder->itemHandler->getDefaultViewSettings(folder, &instance->viewSettings); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 6d2109b..2fd3470 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -420,6 +420,7 @@ void HeapDuplicate(void **pointer, size_t *outBytes, const void *data, size_t by struct EsWindow : EsElement { EsHandle handle; + EsWindowStyle windowStyle; uint32_t windowWidth, windowHeight; bool willUpdate, toolbarFillMode, destroyInstanceAfterClose, hasDialog, doNotPaint; @@ -427,10 +428,11 @@ struct EsWindow : EsElement { bool hovering, activated; bool visualizeRepaints, visualizeLayoutBounds, visualizePaintSteps; // Inspector properties. - EsPoint mousePosition; - EsElement *mainPanel, *toolbar; EsPanel *toolbarSwitcher; + EsElement *dialogOverlay, *dialogPanel; + + EsPoint mousePosition; EsElement *hovered, *pressed, @@ -449,9 +451,9 @@ struct EsWindow : EsElement { Array checkVisible; bool processCheckVisible; - EsElement *dialogOverlay, *dialogPanel; - EsWindowStyle windowStyle; - EsRectangle beforeMaximiseBounds; + EsRectangle beforeMaximiseBounds, targetBounds, animateFromBounds; + bool animateToTargetBoundsAfterResize; + double animateToTargetBoundsTimeMs; EsRectangle updateRegion; EsRectangle updateRegionInProgress; // For visualizePaintSteps. @@ -561,20 +563,14 @@ void WindowChangeBounds(int direction, int newX, int newY, int *originalX, int * EsRectangle screen; EsSyscall(ES_SYSCALL_SCREEN_WORK_AREA_GET, 0, (uintptr_t) &screen, 0, 0); - int newWidth = bounds.r - bounds.l; - int newHeight = bounds.b - bounds.t; - int windowSnapRange = GetConstantNumber("windowSnapRange"); int windowMinimumWidth = GetConstantNumber("windowMinimumWidth"); int windowMinimumHeight = GetConstantNumber("windowMinimumHeight"); int windowRestoreDragYPosition = GetConstantNumber("windowRestoreDragYPosition"); window->isMaximised = false; - - if (newWidth < windowMinimumWidth && direction & RESIZE_LEFT) bounds.l = bounds.r - windowMinimumWidth; - if (newWidth < windowMinimumWidth && direction & RESIZE_RIGHT) bounds.r = bounds.l + windowMinimumWidth; - if (newHeight < windowMinimumHeight && direction & RESIZE_TOP) bounds.t = bounds.b - windowMinimumHeight; - if (newHeight < windowMinimumHeight && direction & RESIZE_BOTTOM) bounds.b = bounds.t + windowMinimumHeight; + window->animateToTargetBoundsAfterResize = false; + window->animateToTargetBoundsTimeMs = -1; if (direction == RESIZE_MOVE) { if (newY < screen.t + windowSnapRange && canSnap) { @@ -605,6 +601,17 @@ void WindowChangeBounds(int direction, int newX, int newY, int *originalX, int * bounds.b = bounds.t + oldHeight; } } else { + EsRectangle targetBounds = bounds; +#define WINDOW_CLAMP_SIZE(_size, _direction, _side, _target) \ + if (_size(bounds) < windowMinimum ## _size && (direction & _direction)) targetBounds._side = _target, bounds._side = RubberBand(bounds._side, _target) + WINDOW_CLAMP_SIZE(Width, RESIZE_LEFT, l, bounds.r - windowMinimumWidth); + WINDOW_CLAMP_SIZE(Width, RESIZE_RIGHT, r, bounds.l + windowMinimumWidth); + WINDOW_CLAMP_SIZE(Height, RESIZE_TOP, t, bounds.b - windowMinimumHeight); + WINDOW_CLAMP_SIZE(Height, RESIZE_BOTTOM, b, bounds.t + windowMinimumHeight); + + window->animateToTargetBoundsAfterResize = !EsRectangleEquals(targetBounds, bounds); + window->animateFromBounds = bounds; + window->targetBounds = targetBounds; window->resetPositionOnNextMove = window->restoreOnNextMove = false; } @@ -658,7 +665,25 @@ int ProcessWindowBorderMessage(EsWindow *window, EsMessage *message, EsRectangle window->resetPositionOnNextMove = true; } + if (window->animateToTargetBoundsAfterResize) { + window->animateToTargetBoundsTimeMs = 0; + window->StartAnimating(); + } + gui.resizing = false; + } else if (message->type == ES_MSG_ANIMATE && window->animateToTargetBoundsAfterResize) { + double progress = window->animateToTargetBoundsTimeMs / 100.0; + window->animateToTargetBoundsTimeMs += message->animate.deltaMs; + + if (progress > 1 || progress < 0) { + message->animate.complete = true; + window->animateToTargetBoundsAfterResize = false; + } else { + progress = SmoothAnimationTimeSharp(progress); + EsRectangle bounds = EsRectangleLinearInterpolate(window->animateFromBounds, window->targetBounds, progress); + EsSyscall(ES_SYSCALL_WINDOW_MOVE, window->handle, (uintptr_t) &bounds, 0, ES_WINDOW_MOVE_DYNAMIC); + message->animate.complete = false; + } } else { return 0; } diff --git a/desktop/os.header b/desktop/os.header index 3898967..7b8f90f 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -2041,6 +2041,7 @@ function EsRectangle EsRectangleCenter(EsRectangle parent, EsRectangle child); function EsRectangle EsRectangleCut(EsRectangle a, int32_t amount, char side); function EsRectangle EsRectangleFit(EsRectangle parent, EsRectangle child, bool allowScalingUp); // Preserves aspect ratio. function EsRectangle EsRectangleIntersection(EsRectangle a, EsRectangle b); +function EsRectangle EsRectangleLinearInterpolate(EsRectangle a, EsRectangle b, float progress); function EsRectangle EsRectangleSplit(EsRectangle *a, int32_t amount, char side, int32_t gap = 0); // Same as EsRectangleCut, but the source rectangle is modified. function EsRectangle EsRectangleSubtract(EsRectangle a, EsRectangle b); function EsRectangle EsRectangleTranslate(EsRectangle a, EsRectangle b); @@ -2199,6 +2200,8 @@ function bool EsCRTisnanf(float f); function int EsCRTisspace(int c); function int EsCRTisupper(int c); function int EsCRTisxdigit(int c); +function double EsCRTlog2(double x); +function float EsCRTlog2f(float x); function void *EsCRTmalloc(size_t size); function void *EsCRTmemchr(const void *_s, int _c, size_t n); function int EsCRTmemcmp(const void *s1, const void *s2, size_t n); diff --git a/shared/math.cpp b/shared/math.cpp index 9182adc..f8e88bf 100644 --- a/shared/math.cpp +++ b/shared/math.cpp @@ -64,6 +64,22 @@ inline int MinimumInteger(int a, int b) { return a < b ? a : b; } +inline float AbsoluteFloat(float f) { + return f > 0 ? f : -f; +} + +inline float SignFloat(float f) { + return f < 0 ? -1 : f > 0 ? 1 : 0; +} + +inline int AbsoluteInteger(int a) { + return a > 0 ? a : -a; +} + +inline int64_t AbsoluteInteger64(int64_t a) { + return a > 0 ? a : -a; +} + ///////////////////////////////// // Interpolation. ///////////////////////////////// @@ -79,6 +95,18 @@ float LinearInterpolate(float from, float to, float progress) { return from + progress * (to - from); } +EsRectangle EsRectangleLinearInterpolate(EsRectangle from, EsRectangle to, float progress) { + return ES_RECT_4(LinearInterpolate(from.l, to.l, progress), LinearInterpolate(from.r, to.r, progress), + LinearInterpolate(from.t, to.t, progress), LinearInterpolate(from.b, to.b, progress)); +} + +float RubberBand(float original, float target) { + float sign = SignFloat(original - target); + float distance = AbsoluteFloat(original - target); + float amount = EsCRTlog2f(distance); + return target + sign * amount * 2.0f; +} + #ifndef KERNEL float GammaInterpolate(float from, float to, float progress) { from = from * from; @@ -184,18 +212,6 @@ float EsCRTfloorf(float x) { return convert.f; } -inline float AbsoluteFloat(float f) { - return f > 0 ? f : -f; -} - -inline int AbsoluteInteger(int a) { - return a > 0 ? a : -a; -} - -inline int64_t AbsoluteInteger64(int64_t a) { - return a > 0 ? a : -a; -} - double EsCRTfloor(double x) { if (x == 0) return x; diff --git a/util/api_table.ini b/util/api_table.ini index b35a76b..04fdc76 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -155,6 +155,7 @@ EsStringAllocateAndFormat=153 EsStringAllocateAndFormatV=154 EsStringCompare=155 EsStringCompareRaw=156 +EsCRTlog2=157 EsStringFormat=158 EsStringFormatTemporary=159 EsStringFormatV=160 @@ -198,6 +199,7 @@ EsCRTqsort=197 EsCRTrealloc=198 EsCRTsinf=199 EsElementGetScaleFactor=200 +EsCRTlog2f=201 EsCRTsqrt=202 EsCRTsqrtf=203 EsSplitterCreate=204 @@ -436,3 +438,4 @@ EsUserTaskSetProgress=436 EsUserTaskIsRunning=437 EsTextboxSetFont=438 EsTextboxSetTextSize=439 +EsRectangleLinearInterpolate=440