fixed: progress indicators

This commit is contained in:
daboe01
2026-01-15 07:47:10 +01:00
parent b96a4e5b94
commit 49923fa140
2 changed files with 272 additions and 77 deletions
+118 -32
View File
@@ -43,7 +43,6 @@ CPProgressIndicatorSpinningStyle = 1;
*/
CPProgressIndicatorHUDBarStyle = 2;
var CPProgressIndicatorSpinningStyleColors = [];
/*!
@ingroup appkit
@@ -71,6 +70,35 @@ var CPProgressIndicatorSpinningStyleColors = [];
BOOL _isDisplayedWhenStopped;
}
// Inject CSS Keyframes for spinning animation (Standard + WebKit)
+ (void)initialize
{
if (self !== [CPProgressIndicator class])
return;
#if PLATFORM(DOM)
if (document.getElementById("cp-progress-indicator-style"))
return;
var style = document.createElement("style");
style.id = "cp-progress-indicator-style";
style.type = "text/css";
var css = "@keyframes cp-progress-indicator-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } " +
"@-webkit-keyframes cp-progress-indicator-spin { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } }";
if (style.styleSheet)
style.styleSheet.cssText = css;
else
style.appendChild(document.createTextNode(css));
var head = document.getElementsByTagName("head")[0];
if (head)
head.appendChild(style);
#endif
}
+ (CPString)defaultThemeClass
{
return @"progress-indicator";
@@ -83,12 +111,19 @@ var CPProgressIndicatorSpinningStyleColors = [];
@"bar-color": [CPNull null],
@"default-height": 20,
@"bezel-color": [CPNull null],
@"spinning-mini-gif": [CPNull null],
@"spinning-small-gif": [CPNull null],
@"spinning-regular-gif": [CPNull null],
// CSS Spinner Attributes
@"spinner-color": [CPColor grayColor],
@"spinner-track-color": [CPColor colorWithWhite:0.9 alpha:1.0],
@"spinner-line-width": 3.0,
@"circular-border-color": [CPNull null],
@"circular-border-size": 1,
@"circular-color": [CPNull null]
@"circular-color": [CPNull null],
@"spinning-mini-gif": [CPNull null],
@"spinning-small-gif": [CPNull null],
@"spinning-regular-gif": [CPNull null]
};
}
@@ -138,6 +173,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
_isAnimating = YES;
[self _hideOrDisplay];
[self setNeedsLayout]; // Trigger layout to update CSS animation state
}
/*!
@@ -149,6 +185,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
_isAnimating = NO;
[self _hideOrDisplay];
[self setNeedsLayout]; // Trigger layout to remove CSS animation state
}
/*!
@@ -233,7 +270,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
_controlSize = aControlSize;
[self updateBackgroundColor];
[self setNeedsLayout];
}
/*!
@@ -285,7 +322,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
_indeterminate = indeterminate;
[self updateBackgroundColor];
[self setNeedsLayout];
}
/*!
@@ -307,9 +344,10 @@ var CPProgressIndicatorSpinningStyleColors = [];
_style = aStyle;
[self setTheme:(_style === CPProgressIndicatorHUDBarStyle) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
if (_style === CPProgressIndicatorSpinningStyle)
[self startAnimation:nil];
[self updateBackgroundColor];
[self setNeedsLayout];
}
/*!
@@ -318,7 +356,14 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)sizeToFit
{
if (_style == CPProgressIndicatorSpinningStyle)
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
{
var size = 32.0;
if (_controlSize === CPMiniControlSize) size = 16.0;
else if (_controlSize === CPSmallControlSize) size = 24.0;
else if (_controlSize === CPRegularControlSize) size = 32.0;
[self setFrameSize:CGSizeMake(size, size)];
}
else
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self valueForThemeAttribute:@"default-height"])];
}
@@ -381,6 +426,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
{
// Handle the standard Bar View
if (aViewName === @"bar-view" && _style !== CPProgressIndicatorSpinningStyle)
{
var width = CGRectGetWidth([self bounds]),
@@ -394,41 +440,78 @@ var CPProgressIndicatorSpinningStyleColors = [];
return CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
}
return nil;
}
/* @ignore */
- (void)updateBackgroundColor
{
if ([CPProgressIndicatorSpinningStyleColors count] === 0)
// Handle the Spinning View (CSS Spinner)
if (aViewName === @"spinner-view" && _style == CPProgressIndicatorSpinningStyle && _indeterminate)
{
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
return [self bounds];
}
[self setNeedsLayout];
// Return nil for views that shouldn't appear in the current style
return nil;
}
- (void)layoutSubviews
{
if (YES)//_isBezeled)
{
// === SPINNING STYLE ===
if (_style == CPProgressIndicatorSpinningStyle)
{
if (!_indeterminate)
return;
// If indeterminate, use CSS spinner
if (_indeterminate)
{
var spinnerView = [self layoutEphemeralSubviewNamed:"spinner-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:nil];
// Ensure other views are hidden
[self layoutEphemeralSubviewNamed:"bar-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
[self layoutEphemeralSubviewNamed:"bar-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:nil];
// Configure CSS on the subview, NOT self._DOMElement, to avoid transform conflicts
var domEl = spinnerView._DOMElement,
spinnerColor = [self currentValueForThemeAttribute:@"spinner-color"],
trackColor = [self currentValueForThemeAttribute:@"spinner-track-color"],
lineWidth = [self currentValueForThemeAttribute:@"spinner-line-width"],
widthStr = lineWidth + "px";
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
domEl.style.boxSizing = "border-box";
domEl.style.borderRadius = "50%";
domEl.style.borderStyle = "solid";
domEl.style.borderWidth = widthStr;
domEl.style.borderColor = [trackColor cssString];
domEl.style.borderTopColor = [spinnerColor cssString];
// Animation logic
if (_isAnimating)
{
var anim = "cp-progress-indicator-spin 1s linear infinite";
domEl.style.animation = anim;
domEl.style.WebkitAnimation = anim;
}
else
{
domEl.style.animation = "none";
domEl.style.WebkitAnimation = "none";
}
// Ensure main view is transparent
[self setBackgroundColor:nil];
}
else
{
// Determinate Spinner (Pie Chart drawn in drawRect)
// Hide CSS spinner
[self layoutEphemeralSubviewNamed:"spinner-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
[self setBackgroundColor:nil];
}
}
// === BAR STYLE ===
else
{
// Hide spinner
[self layoutEphemeralSubviewNamed:"spinner-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
var barView = [self layoutEphemeralSubviewNamed:"bar-view"
@@ -447,6 +530,8 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)drawRect:(CGRect)aRect
{
// Handle determinate state for Spinning style (Pie chart progress) via CoreGraphics.
// If indeterminate, the CSS animation (spinner-view) takes over and we draw nothing.
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
@@ -512,7 +597,10 @@ var CPProgressIndicatorSpinningStyleColors = [];
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
_isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
[self updateBackgroundColor];
if (_style === CPProgressIndicatorSpinningStyle)
[self startAnimation:nil];
[self setNeedsLayout];
}
return self;
@@ -520,8 +608,6 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)encodeWithCoder:(CPCoder)aCoder
{
// Don't encode the background colour. It can be recreated based on the flags
// and if encoded causes hardcoded image paths in the cib while just wasting space.
var backgroundColor = [self backgroundColor];
[self setBackgroundColor:nil];
[super encodeWithCoder:aCoder];
+154 -45
View File
@@ -6995,24 +6995,51 @@ var themedButtonValues = nil,
+ (CPProgressIndicator)themedBarProgressIndicator
{
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 75, 20)];
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
[progressBar setDoubleValue:30];
// Using CSS for progress bars
// --- Standard Colors ---
var bezelColor = [CPColor colorWithCSSDictionary:@{
@"border": @"1px solid " + A3ColorBorderDark,
@"background-color": A3ColorBackground
}],
@"border": @"1px solid " + A3ColorBorderDark,
@"background-color": A3ColorBackgroundWhite,
@"border-radius": @"3px",
@"box-sizing": @"border-box"
}],
barColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"#5982DA"
}];
barColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"#5982DA",
@"border-radius": @"2px"
}],
// --- HUD Colors ---
hudBezelColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"rgba(0, 0, 0, 0.2)",
@"border": @"1px solid rgba(255, 255, 255, 0.2)",
@"border-radius": @"3px",
@"box-sizing": @"border-box",
@"box-shadow": @"inset 0 1px 2px rgba(0,0,0,0.1)"
}],
hudBarColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"rgba(255, 255, 255, 0.9)",
@"border-radius": @"2px",
@"box-shadow": @"0 0 4px rgba(255, 255, 255, 0.4)"
}];
themedProgressIndicator =
[
[@"bezel-color", bezelColor],
[@"bar-color", barColor],
[@"default-height", 20]
// Standard State
[@"bezel-color", bezelColor],
[@"bar-color", barColor],
// HUD State
[@"bezel-color", hudBezelColor, CPThemeStateHUD],
[@"bar-color", hudBarColor, CPThemeStateHUD],
// Control Sizes (Heights)
[@"default-height", 20.0], // Regular
[@"default-height", 16.0, CPThemeStateControlSizeSmall],
[@"default-height", 10.0, CPThemeStateControlSizeMini]
];
[self registerThemeValues:themedProgressIndicator forView:progressBar];
@@ -7022,24 +7049,54 @@ var themedButtonValues = nil,
+ (CPProgressIndicator)themedIndeterminateBarProgressIndicator
{
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 75, 20)];
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
[progressBar setIndeterminate:YES];
// --- Standard Colors ---
var bezelColor = [CPColor colorWithCSSDictionary:@{
@"border": @"1px solid " + A3ColorBorderDark,
@"background-color": A3ColorBackground
}],
@"border": @"1px solid " + A3ColorBorderDark,
@"background-color": A3ColorBackgroundWhite,
@"border-radius": @"3px",
@"box-sizing": @"border-box"
}],
barColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"#5982DA"
}];
// Ideally this would be a striped CSS gradient animation,
// but for now we use the solid blue matching Aristo3 style
barColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"#5982DA",
@"border-radius": @"2px",
@"opacity": @"0.8"
}],
// --- HUD Colors ---
hudBezelColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"rgba(0, 0, 0, 0.2)",
@"border": @"1px solid rgba(255, 255, 255, 0.2)",
@"border-radius": @"3px",
@"box-sizing": @"border-box",
@"box-shadow": @"inset 0 1px 2px rgba(0,0,0,0.1)"
}],
hudBarColor = [CPColor colorWithCSSDictionary:@{
@"background-color": @"rgba(255, 255, 255, 0.8)",
@"border-radius": @"2px",
@"box-shadow": @"0 0 4px rgba(255, 255, 255, 0.4)"
}];
themedIndeterminateProgressIndicator =
[
[@"bezel-color", bezelColor],
[@"indeterminate-bar-color", barColor],
[@"default-height", 20]
// Standard State
[@"bezel-color", bezelColor],
[@"indeterminate-bar-color", barColor],
// HUD State
[@"bezel-color", hudBezelColor, CPThemeStateHUD],
[@"indeterminate-bar-color", hudBarColor, CPThemeStateHUD],
// Control Sizes
[@"default-height", 20.0], // Regular
[@"default-height", 16.0, CPThemeStateControlSizeSmall],
[@"default-height", 10.0, CPThemeStateControlSizeMini]
];
[self registerThemeValues:themedIndeterminateProgressIndicator forView:progressBar];
@@ -7049,20 +7106,33 @@ var themedButtonValues = nil,
+ (CPProgressIndicator)themedSpinningProgressIndicator
{
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
[progressBar setIndeterminate:YES];
[progressBar startAnimation:nil];
// Keep GIFs for complex spinning animation as CSS/SVG spinning requires code support in CPProgressIndicator
// Assuming Aristo3Colors doesn't define these, we reference local resources
var spinningMini = PatternColor(@"progress-indicator-spinning-style-mini.gif", 16.0, 16.0),
spinningSmall = PatternColor(@"progress-indicator-spinning-style-small.gif", 32.0, 32.0),
spinningRegular = PatternColor(@"progress-indicator-spinning-style-regular.gif", 64.0, 64.0),
// Standard Colors (Grey)
var spinnerColor = [CPColor colorWithHexString:@"888888"],
spinnerTrackColor = [CPColor colorWithHexString:@"e6e6e6"],
// HUD Colors (White/Transparent)
hudSpinnerColor = [CPColor whiteColor],
hudSpinnerTrackColor = [CPColor colorWithWhite:1.0 alpha:0.2];
themeValues =
var themeValues =
[
[@"spinning-mini-gif", spinningMini],
[@"spinning-small-gif", spinningSmall],
[@"spinning-regular-gif", spinningRegular]
// --- Standard Theme ---
[@"spinner-color", spinnerColor],
[@"spinner-track-color", spinnerTrackColor],
// Line widths based on size
[@"spinner-line-width", 4.0], // Regular
[@"spinner-line-width", 3.0, CPThemeStateControlSizeSmall], // Small
[@"spinner-line-width", 2.0, CPThemeStateControlSizeMini], // Mini
// --- HUD Theme ---
[@"spinner-color", hudSpinnerColor, CPThemeStateHUD],
[@"spinner-track-color", hudSpinnerTrackColor, CPThemeStateHUD]
];
[self registerThemeValues:themeValues forView:progressBar];
@@ -7333,11 +7403,11 @@ var themedButtonValues = nil,
var standardWindowView = [[_CPStandardWindowView alloc] initWithFrame:CGRectMake(0, 0, 200, 200) styleMask:CPClosableWindowMask],
bezelHeadCssColor = [CPColor colorWithCSSDictionary:@{
@"background-color": A3ColorWindowHeadActive,
@"background-color": A3ColorWindowHeadActive,
@"border-top-color": A3ColorWindowBorder,
@"border-top-style": @"solid",
@"border-top-width": @"1px",
@"border-left-color": A3ColorWindowBorder,
@"border-left-color": A3ColorWindowBorder,
@"border-left-style": @"solid",
@"border-left-width": @"1px",
@"border-right-color": A3ColorWindowBorder,
@@ -7351,11 +7421,11 @@ var themedButtonValues = nil,
}],
inactiveBezelHeadCssColor = [CPColor colorWithCSSDictionary:@{
@"background-color": A3ColorWindowHeadInactive,
@"border-top-color": A3ColorWindowBorder,
@"background-color": A3ColorWindowHeadInactive,
@"border-top-color": A3ColorWindowBorder,
@"border-top-style": @"solid",
@"border-top-width": @"1px",
@"border-left-color": A3ColorWindowBorder,
@"border-left-color": A3ColorWindowBorder,
@"border-left-style": @"solid",
@"border-left-width": @"1px",
@"border-right-color": A3ColorWindowBorder,
@@ -7374,7 +7444,7 @@ var themedButtonValues = nil,
bezelCssColor = [CPColor colorWithCSSDictionary:@{
@"background-color": A3ColorBackground,
@"border-color": A3ColorWindowBorder,
@"border-color": A3ColorWindowBorder,
@"border-style": @"solid",
@"border-width": @"1px",
@"border-top-left-radius": @"0px",
@@ -7388,30 +7458,63 @@ var themedButtonValues = nil,
@"background-color": A3ColorBorderMedium
}],
// Pure CSS Close button
// --- BUTTON DEFINITIONS (Normal vs Highlighted/Pressed) ---
// Note: We include the 'transition' property so the color change animates when clicked.
// 1. Close Button (Red)
closeButtonImage = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonClose,
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px"
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
// Pure CSS Minimize button
closeButtonImageHighlighted = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonCloseDark, // Darker Red
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
// 2. Minimize Button (Yellow)
minimizeButtonImage = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonMin,
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px"
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
// Pure CSS Zoom button
minimizeButtonImageHighlighted = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonMinDark, // Darker Yellow
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
// 3. Zoom Button (Green)
zoomButtonImage = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonZoom,
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px"
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
zoomButtonImageHighlighted = [CPImage imageWithCSSDictionary:@{
@"background-color": A3ColorWindowButtonZoomDark, // Darker Green
@"border-radius": @"50%",
@"width": @"12px",
@"height": @"12px",
@"transition": @"background-color 0.15s ease-in-out"
}
size:CGSizeMake(12,12)],
@@ -7427,7 +7530,7 @@ var themedButtonValues = nil,
[@"bezel-head-color", inactiveBezelHeadCssColor, CPThemeStateNormal],
[@"bezel-head-color", bezelHeadCssColor, CPThemeStateKeyWindow],
[@"bezel-head-color", bezelHeadCssColor, CPThemeStateMainWindow],
[@"bezel-head-sheet-color", [CPColor redColor]],
[@"bezel-head-sheet-color", [CPColor redColor]],
[@"solid-color", solidCssColor],
[@"title-font", [CPFont systemFontOfSize:CPFontCurrentSystemSize+1]],
@@ -7446,10 +7549,16 @@ var themedButtonValues = nil,
[@"title-margin", 4],
[@"frame-outset", CGInsetMake(1, 1, 1, 1)],
// --- REGISTER NORMAL STATES ---
[@"close-image-button", closeButtonImage],
[@"minimize-image-button", minimizeButtonImage],
[@"zoom-image-button", zoomButtonImage],
// --- REGISTER PRESSED STATES (Using CPThemeStateHighlighted) ---
[@"close-image-button", closeButtonImageHighlighted, CPThemeStateHighlighted],
[@"minimize-image-button", minimizeButtonImageHighlighted, CPThemeStateHighlighted],
[@"zoom-image-button", zoomButtonImageHighlighted, CPThemeStateHighlighted],
[@"close-image-size", CGSizeMake(12.0, 12.0)],
[@"close-image-origin", CGPointMake(10.0, 10.0)],
[@"minimize-image-size", CGSizeMake(12.0, 12.0)],