AppKit: Replace OS behaviour API with theme-driven window frame layout

Eliminate brittle platform-specific branching and the incomplete host-platform OS behaviour API, replacing them with a unified layout model where window frames, titles, and controls derive entirely from the active theme, with platform variants selected through a private function.

* CPApplication.j:
  - Remove setOSBehavior:, OSBehavior, shouldMimicWindows, and simulatesWindows accessors, along with associated Info.plist keys and user defaults.
  - Set default theme selection through _CPThemeVariant.

* CPApplication_Constants.j: Remove OS behaviour constants, dictionary, and notification.

* CPCompatibility.j: Add _CPBrowserOperatingSystem to return the detected host operating system.

* CPTheme.j: Remove CPThemeStateWindowsPlatform; add _CPThemeVariant to return the base theme.

* CPWindow.j: Remove the _close delegation to the window view.

* _CPStandardWindowView.j:
  - Remove OS behaviour observer, _close, and _osBehaviorDidChange:.
  - Position buttons and title using theme values during layout, applying fallback defaults when theme values are absent.
  - Read title bar and sheet values from the view's active theme rather than the default theme.

* _CPTitleableWindowView.j: Query title-margin dynamically during layout instead of caching.
This commit is contained in:
David Richardson committed 2026-11-08 13:19:54 -06:00
1 parent 073b18b162
commit c062669fb5
7 files changed
+126 -288

No files matched your search

+2 -72
View File
@@ -127,10 +127,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
CPMenuItem _windowsMenuSeparator;
CPThemeBlend _themeBlend @accessors(property=themeBlend);
// OS behavior
CPApplicationOSBehavior _OSBehavior;
BOOL _simulatesWindows;
}
/*!
@@ -163,9 +159,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
_eventListenerInsertionIndex = 0;
_windows = [[CPNull null]];
_OSBehavior = CPApplicationLegacyOSBehavior;
_simulatesWindows = NO;
}
return self;
@@ -1503,44 +1496,6 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo3");
}
// See CPApplication_Constants.j for comments
- (void)setOSBehavior:(CPApplicationOSBehavior)anOSBehavior
{
// Verify if provided OS behavior is valid
if ([[CPApplicationOSBehaviors allKeysForObject:anOSBehavior] count] == 0)
{
CPLog.warn("CPApplication setOSBehavior: invalid CPApplicationOSBehavior (received "+anOSBehavior+"). Ignored.");
return;
}
_OSBehavior = anOSBehavior;
[[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationOSBehaviorDidChangeNotification object:CPApp userInfo:nil];
}
- (CPApplicationOSBehavior)OSBehavior
{
return _OSBehavior;
}
- (BOOL)shouldMimicWindows
{
return (_OSBehavior == CPApplicationFollowOSBehavior) && (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) || _simulatesWindows);
}
- (void)setSimulatesWindows:(BOOL)shouldSimulateWindows
{
if (_simulatesWindows === shouldSimulateWindows)
return;
_simulatesWindows = shouldSimulateWindows;
}
- (BOOL)simulatesWindows
{
return _simulatesWindows;
}
@end
var _CPModalSessionMake = function(aWindow, aStopCode)
@@ -1667,35 +1622,10 @@ var _CPAppBootstrapperActions = nil;
+ (void)blendDidFinishLoading:(CPThemeBlend)aThemeBlend
{
[[CPApplication sharedApplication] setThemeBlend:aThemeBlend];
[CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]];
// Search in the Info.plist if the special CPApplicationSimulateWindowsOS flag is set (for testing)
[CPApp setSimulatesWindows:!![[CPBundle mainBundle] objectForInfoDictionaryKey:"CPApplicationSimulateWindowsOS"]];
var baseTheme = [CPTheme themeNamed:[CPApplication defaultThemeName]];
// Before loading the main CIB, try to find if a CPApplicationOSBehavior is specified in the Info.plist or in the user defaults
// (with user defaults precedence). Value stored must be a string representing the name of the OS behavior.
var plistOSBehavior = [[CPBundle mainBundle] objectForInfoDictionaryKey:"CPApplicationOSBehavior"],
userOSBehavior = [[CPUserDefaults standardUserDefaults] objectForKey:@"CPApplicationOSBehavior"];
if (userOSBehavior)
{
var osBehavior = [CPApplicationOSBehaviors objectForKey:userOSBehavior];
if (osBehavior)
[CPApp setOSBehavior:osBehavior];
else
CPLog.warn("Invalid CPApplicationOSBehavior specified in user defaults (found:"+userOSBehavior+"). Ignored.");
}
else if (plistOSBehavior)
{
var osBehavior = [CPApplicationOSBehaviors objectForKey:plistOSBehavior];
if (osBehavior)
[CPApp setOSBehavior:osBehavior];
else
CPLog.warn("Invalid CPApplicationOSBehavior specified in Info.plist (found:"+plistOSBehavior+"). Ignored.");
}
[CPTheme setDefaultTheme:_CPThemeVariant(baseTheme, nil, _CPBrowserOperatingSystem())];
[self performActions];
}
-12
View File
@@ -39,15 +39,3 @@ CPTerminateLater = -1; // not currently supported
CPRunStoppedResponse = -1000;
CPRunAbortedResponse = -1001;
CPRunContinuesResponse = -1002;
// Should the application follow Cappuccino UX-UI (which is OSX like) or OS UX-UI (mainly Windows) ?
// See explanation on https://github.com/cappuccino/cappuccino/wiki/CPApplicationSelectedOSBehavior
@typedef CPApplicationOSBehavior
CPApplicationLegacyOSBehavior = 1;
CPApplicationFollowOSBehavior = 2;
CPApplicationOSBehaviorDidChangeNotification = @"CPApplicationOSBehaviorDidChangeNotification";
CPApplicationOSBehaviors = @{
@"CPApplicationLegacyOSBehavior": CPApplicationLegacyOSBehavior,
@"CPApplicationFollowOSBehavior": CPApplicationFollowOSBehavior
};
+8
View File
@@ -355,6 +355,14 @@ function CPBrowserIsOperatingSystem(anOperatingSystem)
return OPERATING_SYSTEM === anOperatingSystem;
}
/*
Returns the host operating system, as detected when this file loads.
*/
function _CPBrowserOperatingSystem()
{
return OPERATING_SYSTEM;
}
OPERATING_SYSTEM = CPOtherOperatingSystem;
if (USER_AGENT.indexOf("Mac") !== -1)
+13 -1
View File
@@ -291,6 +291,18 @@ var CPThemesByName = { },
@end
/*
Returns the variant of a theme for an appearance and a host platform. The variants
of one theme share its metrics. They differ in colours and images, and in the place
of decoration such as the window buttons and the title. This function selects no
variant yet: it returns the base theme for every appearance and every host, so
every host has the base theme's look.
*/
function _CPThemeVariant(aBaseTheme, anAppearance, aPlatform)
{
return aBaseTheme;
}
var CPThemeNameKey = @"CPThemeNameKey",
CPThemeAttributesKey = @"CPThemeAttributesKey";
@@ -817,7 +829,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
CPThemeStateControlSizeLarge = CPThemeState("controlSizeLarge");
CPThemeStateAlternateState = CPThemeState("alternate");
CPThemeStateComposedControl = CPThemeState("composed");
CPThemeStateWindowsPlatform = CPThemeState("windowsPlatform");
CPThemeStateNormalString = String(CPThemeStateNormal);
CPThemeStateHUD = CPThemeState("hud");
-4
View File
@@ -2614,10 +2614,6 @@ CPTexturedBackgroundWindowMask
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self];
// Give a chance to the _windowView to do things before closing (for example, remove an observer)
if ([_windowView respondsToSelector:@selector(_close)])
[_windowView _close];
[_parentWindow removeChildWindow:self];
[self _orderOutRecursively:NO];
[self _detachFromChildrenClosing:!_parentWindow];
+89 -192
View File
@@ -22,7 +22,6 @@
@import "CPButton.j"
@import "_CPTitleableWindowView.j"
@import "CPApplication_Constants.j"
@class _CPDocModalWindowView
@@ -68,13 +67,18 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
return self;
}
/*
The head view draws with the theme values of its window view, in the window view's
current state, so it follows the window's theme. It read them from the default
theme, so under any other theme it drew the default theme's height and colours.
*/
- (void)layoutSubviews
{
[super layoutSubviews];
var gradientHeight = [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView],
var gradientHeight = [_parentView valueForThemeAttribute:@"gradient-height"],
bounds = [self bounds],
bezelHeadColor = [[CPTheme defaultTheme] valueForAttributeWithName:_isSheet ? @"bezel-head-sheet-color" : @"bezel-head-color" inState:[_parentView themeState] forClass:_CPStandardWindowView];
bezelHeadColor = [_parentView currentValueForThemeAttribute:_isSheet ? @"bezel-head-sheet-color" : @"bezel-head-color"];
// Apply the border/background color to self (the container) so borders are drawn on the outside
[self setBackgroundColor:bezelHeadColor];
@@ -84,18 +88,19 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
// Set x=0.0 to avoid 1px gap. Width is reduced by 2.0.
[_solidView setFrame:CGRectMake(0.0, gradientHeight, CGRectGetWidth(bounds) - 2.0, CGRectGetHeight(bounds) - gradientHeight)];
[_solidView setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"solid-color" forClass:_CPStandardWindowView]];
[_solidView setBackgroundColor:[_parentView currentValueForThemeAttribute:@"solid-color"]];
}
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
{
var bounds = [self bounds],
width = CGRectGetWidth(bounds) - 2.0; // Account for border inset
width = CGRectGetWidth(bounds) - 2.0, // Account for border inset
gradientHeight = [_parentView valueForThemeAttribute:@"gradient-height"];
if (width < 0) width = 0;
[_gradientView setFrameSize:CGSizeMake(width, [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
[_solidView setFrameSize:CGSizeMake(width, CGRectGetHeight(bounds) - [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
[_gradientView setFrameSize:CGSizeMake(width, gradientHeight)];
[_solidView setFrameSize:CGSizeMake(width, CGRectGetHeight(bounds) - gradientHeight)];
}
@end
@@ -235,22 +240,14 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
[self addSubview:_minimizeButton];
}
[self _updateWindowButtons:YES];
[self _updateWindowButtons];
[self updateTrackingAreas];
[self tile];
// Observe CPApplicationOSBehaviorDidChangeNotification
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_osBehaviorDidChange:) name:CPApplicationOSBehaviorDidChangeNotification object:CPApp];
}
return self;
}
// This will be called by CPWindow -close so the observer can be removed
- (void)_close
{
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPApplicationOSBehaviorDidChangeNotification object:CPApp];
}
- (void)viewDidMoveToWindow
{
[_closeButton setTarget:[self window]];
@@ -293,168 +290,64 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
[_bodyView setFrame:CGRectMake(0.0, dividerMinY, width, CGRectGetHeight(bounds) - dividerMinY)];
[_titleField setFrame:CGRectMake(_buttonsWidth, 0, width - _buttonsWidth * 2.0, _titleBarHeight)];
[self _layoutTitleField];
var contentFrame = [_bodyView frame];
[[theWindow contentView] setFrame:CGRectInset(contentFrame, 1.0, 1.0)];
}
- (void)_updateWindowButtons:(BOOL)shouldRefreshLayout
/*
The buttons are placed from the theme values on every layout, so a change of theme
moves them. They were placed only when the view was made, so the window kept the
old theme's positions. The positions come from the theme alone, with no branch for
the host platform. A theme without positions gets fixed defaults.
*/
- (void)_updateWindowButtons
{
if (shouldRefreshLayout)
var closeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"close-image-origin"] || CGPointMake(8.0, 8.0)),
minimizeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"minimize-image-origin"] || CGPointMake(27.0, 7.0)),
zoomOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"zoom-image-origin"] || CGPointMake(46.0, 8.0)),
delta1 = minimizeOrigin.x - closeOrigin.x,
delta2 = zoomOrigin.x - minimizeOrigin.x;
_buttonsWidth = 0;
if (_styleMask & CPClosableWindowMask)
{
var mimicWindows = [CPApp shouldMimicWindows];
var closeSize = [self currentValueForThemeAttribute:@"close-image-size"] || CGSizeMake(16.0, 16.0);
if (mimicWindows)
[self setThemeState:CPThemeStateWindowsPlatform];
else
[self unsetThemeState:CPThemeStateWindowsPlatform];
[_closeButton setFrame:CGRectMake(closeOrigin.x, closeOrigin.y, closeSize.width, closeSize.height)];
// Remember that on Cappuccino, buttons are (left to right) : close - minimize - zoom
// and that on Windows, (also left to right) : minimize - zoom - close
var offset,
mask,
closeThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"close-image-origin"] || CGPointMakeZero()),
minimizeThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"minimize-image-origin"] || CGPointMakeZero()),
zoomThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"zoom-image-origin"] || CGPointMakeZero()),
closeThemeSize = CGSizeMakeZero(),
minimizeThemeSize = CGSizeMakeZero(),
zoomThemeSize = CGSizeMakeZero(),
delta1,
delta2;
// For retro-compatibility
if (![self isCSSBased])
{
if (mimicWindows)
{
// There's no zoom button in Aristo2
closeThemeOrigin = CGPointMake(-24.0, 8.0);
minimizeThemeOrigin = CGPointMake(-43.0, 8.0);
// FIXME: if someone designs a zoom button in Aristo2 one day, use those values
// closeThemeOrigin = CGPointMake(-24.0, 8.0);
// minimizeThemeOrigin = CGPointMake(-62.0, 8.0);
// zoomThemeOrigin = CGPointMake(-43.0, 8.0);
}
else
{
closeThemeOrigin = CGPointMake(8.0, 8.0);
minimizeThemeOrigin = CGPointMake(27.0, 8.0);
zoomThemeOrigin = CGPointMake(46.0, 8.0);
}
}
if (mimicWindows)
{
offset = [self bounds].size.width;
mask = CPViewMinXMargin;
delta1 = zoomThemeOrigin.x - closeThemeOrigin.x;
delta2 = minimizeThemeOrigin.x - zoomThemeOrigin.x;
}
else
{
offset = 0;
mask = CPViewMaxXMargin;
delta1 = minimizeThemeOrigin.x - closeThemeOrigin.x;
delta2 = zoomThemeOrigin.x - minimizeThemeOrigin.x;
}
_buttonsWidth = 0;
if (_styleMask & CPClosableWindowMask)
{
closeThemeSize = [self currentValueForThemeAttribute:@"close-image-size"];
// For retro-compatibility:
if (!closeThemeSize)
closeThemeSize = CGSizeMake(16.0, 16.0);
[_closeButton setFrame:CGRectMake(closeThemeOrigin.x + offset, closeThemeOrigin.y, closeThemeSize.width, closeThemeSize.height)];
[_closeButton setAutoresizingMask:mask];
_buttonsWidth = ABS(closeThemeOrigin.x) + (mimicWindows ? 0 : closeThemeSize.width);
}
else
{
minimizeThemeOrigin.x -= delta1;
zoomThemeOrigin.x -= delta1;
}
if (mimicWindows)
{
if (_styleMask & CPResizableWindowMask)
{
zoomThemeSize = [self currentValueForThemeAttribute:@"zoom-image-size"];
// For retro-compatibility:
if (!zoomThemeSize)
zoomThemeSize = CGSizeMake(16.0, 16.0);
[_zoomButton setFrame:CGRectMake(zoomThemeOrigin.x + offset, zoomThemeOrigin.y, zoomThemeSize.width, zoomThemeSize.height)];
[_zoomButton setAutoresizingMask:mask];
_buttonsWidth = ABS(zoomThemeOrigin.x);
}
else
minimizeThemeOrigin.x -= delta2;
if (_styleMask & CPMiniaturizableWindowMask)
{
minimizeThemeSize = [self currentValueForThemeAttribute:@"minimize-image-size"];
// For retro-compatibility:
if (!minimizeThemeSize)
minimizeThemeSize = CGSizeMake(16.0, 16.0);
[_minimizeButton setFrame:CGRectMake(minimizeThemeOrigin.x + offset, minimizeThemeOrigin.y, minimizeThemeSize.width, minimizeThemeSize.height)];
[_minimizeButton setAutoresizingMask:mask];
_buttonsWidth = ABS(minimizeThemeOrigin.x);
}
if (_buttonsWidth > 0)
_buttonsWidth += ABS(closeThemeOrigin.x) - closeThemeSize.width;
}
else // not win
{
if (_styleMask & CPMiniaturizableWindowMask)
{
minimizeThemeSize = [self currentValueForThemeAttribute:@"minimize-image-size"];
// For retro-compatibility:
if (!minimizeThemeSize)
minimizeThemeSize = CGSizeMake(16.0, 16.0);
[_minimizeButton setFrame:CGRectMake(minimizeThemeOrigin.x + offset, minimizeThemeOrigin.y, minimizeThemeSize.width, minimizeThemeSize.height)];
[_minimizeButton setAutoresizingMask:mask];
_buttonsWidth = minimizeThemeOrigin.x + minimizeThemeSize.width;
}
else
zoomThemeOrigin.x -= delta2;
if (_styleMask & CPResizableWindowMask)
{
zoomThemeSize = [self currentValueForThemeAttribute:@"zoom-image-size"];
// For retro-compatibility:
if (!zoomThemeSize)
zoomThemeSize = CGSizeMake(16.0, 16.0);
[_zoomButton setFrame:CGRectMake(zoomThemeOrigin.x + offset, zoomThemeOrigin.y, zoomThemeSize.width, zoomThemeSize.height)];
[_zoomButton setAutoresizingMask:mask];
_buttonsWidth = zoomThemeOrigin.x + zoomThemeSize.width;
}
if (_buttonsWidth > 0)
_buttonsWidth += closeThemeOrigin.x;
}
[self updateTrackingAreas];
_buttonsWidth = closeOrigin.x + closeSize.width;
}
else
{
minimizeOrigin.x -= delta1;
zoomOrigin.x -= delta1;
}
if (_styleMask & CPMiniaturizableWindowMask)
{
var minimizeSize = [self currentValueForThemeAttribute:@"minimize-image-size"] || CGSizeMake(16.0, 16.0);
[_minimizeButton setFrame:CGRectMake(minimizeOrigin.x, minimizeOrigin.y, minimizeSize.width, minimizeSize.height)];
_buttonsWidth = minimizeOrigin.x + minimizeSize.width;
}
else
zoomOrigin.x -= delta2;
if (_styleMask & CPResizableWindowMask)
{
var zoomSize = [self currentValueForThemeAttribute:@"zoom-image-size"] || CGSizeMake(16.0, 16.0);
[_zoomButton setFrame:CGRectMake(zoomOrigin.x, zoomOrigin.y, zoomSize.width, zoomSize.height)];
_buttonsWidth = zoomOrigin.x + zoomSize.width;
}
if (_buttonsWidth > 0)
_buttonsWidth += closeOrigin.x;
[self _updateCloseButton];
@@ -464,6 +357,29 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
[_zoomButton setAlternateImage:[self currentValueForThemeAttribute:@"zoom-image-highlighted-button"]];
}
/*
The title fills the space between the buttons and the same width on the other side.
When that is too narrow for the title, the title gives up the space on the other
side first. The title margin is read from the theme here, so a change of theme
moves the title.
*/
- (void)_layoutTitleField
{
var width = CGRectGetWidth([self bounds]),
titleMargin = [self currentValueForThemeAttribute:@"title-margin"],
titleWidth = width - 2.0 * _buttonsWidth;
if (titleWidth < _minimumTitleFieldSize)
{
if (width - _buttonsWidth - titleMargin < _minimumTitleFieldSize)
titleWidth = width - _buttonsWidth - titleMargin;
else
titleWidth = _minimumTitleFieldSize;
}
[_titleField setFrame:CGRectMake(_buttonsWidth, 0, titleWidth, _titleBarHeight)];
}
- (void)_updateCloseButton
{
if (_isDocumentEdited)
@@ -478,11 +394,6 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
}
}
- (void)_osBehaviorDidChange:(id)application
{
[self _updateWindowButtons:YES];
}
- (void)setDocumentEdited:(BOOL)isEdited
{
_isDocumentEdited = isEdited;
@@ -526,7 +437,7 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
if (enable)
{
[_bodyView setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"body-color" forClass:_CPDocModalWindowView]];
[_bodyView setBackgroundColor:[[self actualTheme] valueForAttributeWithName:@"body-color" forClass:_CPDocModalWindowView]];
// Move the shadow view down so it is inside the content border
var shadowFrame = [_sheetShadowView frame];
@@ -542,7 +453,7 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
dy = [self toolbarMaxY] + dividerHeight;
if (_toolbarView && [self showsToolbar])
dy = [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView];
dy = [self valueForThemeAttribute:@"gradient-height"];
if (enable)
dy = -dy;
@@ -562,26 +473,15 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
- (void)layoutSubviews
{
var width = [self bounds].size.width,
mimicWindows = [CPApp shouldMimicWindows];
[super layoutSubviews];
if (_closeButton || _minimizeButton || _zoomButton)
[self _updateWindowButtons:NO];
[self _updateWindowButtons];
[self _layoutTitleField];
[_dividerView setBackgroundColor:[self valueForThemeAttribute:@"divider-color"]];
[_bodyView setBackgroundColor:[self valueForThemeAttribute:@"body-color"]];
[_headView setNeedsLayout];
if (width - 2 * _buttonsWidth < _minimumTitleFieldSize)
{
if (width - _buttonsWidth - _titleMargin < _minimumTitleFieldSize)
[_titleField setFrame:CGRectMake((mimicWindows ? _titleMargin : _buttonsWidth), 0, width - _buttonsWidth - _titleMargin, _titleBarHeight)];
else
[_titleField setFrame:CGRectMake((mimicWindows ? width - _buttonsWidth - _minimumTitleFieldSize : _buttonsWidth), 0, _minimumTitleFieldSize, _titleBarHeight)];
}
}
- (CGSize)_minimumResizeSize
@@ -650,9 +550,6 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
var triggeredButton = [[anEvent trackingArea] userInfo],
state = CPThemeStateHovered;
if ([CPApp shouldMimicWindows])
state = state.and(CPThemeStateWindowsPlatform);
if (triggeredButton === _closeButton)
{
if (_isDocumentEdited)
+14 -7
View File
@@ -29,7 +29,6 @@
CPTextField _titleField;
int _minimumTitleFieldSize;
int _titleBarHeight;
int _titleMargin;
}
+ (int)titleBarHeight
@@ -67,7 +66,8 @@
{
// We cache some values for optimization
_titleBarHeight = [[self class] titleBarHeight];
_titleMargin = [self currentValueForThemeAttribute:@"title-margin"];
var titleMargin = [self currentValueForThemeAttribute:@"title-margin"];
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
@@ -77,7 +77,7 @@
[_titleField setAutoresizingMask:CPViewWidthSizable];
[self setTitle:@""];
[_titleField setFrame:CGRectMake(_titleMargin, 3.0, CGRectGetWidth([self bounds]) - 2 * _titleMargin, CGRectGetHeight([_titleField frame]))];
[_titleField setFrame:CGRectMake(titleMargin, 3.0, CGRectGetWidth([self bounds]) - 2 * titleMargin, CGRectGetHeight([_titleField frame]))];
[self addSubview:_titleField];
@@ -100,11 +100,18 @@
var theWindow = [self window],
bounds = [self bounds],
width = CGRectGetWidth(bounds);
width = CGRectGetWidth(bounds),
titleMargin = [self currentValueForThemeAttribute:@"title-margin"];
// The vertical alignment of the title is set by the theme, so just give it all available space. By default
// the title will vertically centre within.
[_titleField setFrame:CGRectMake(_titleMargin, 0, width - 2 * _titleMargin, _titleBarHeight)];
/*
The title margin is read from the theme at each tile, so the title follows a
change of theme. It was copied when the view was made, so the title kept the
old theme's margin.
The vertical alignment of the title is set by the theme, so just give it all
available space. By default the title will vertically centre within.
*/
[_titleField setFrame:CGRectMake(titleMargin, 0, width - 2 * titleMargin, _titleBarHeight)];
}
- (void)layoutSubviews