Theme Assets
-
ThemeColor
is aNSColor
subclass that dynamically changes its colors whenever a new theme is make current.Theme-aware means you don’t need to check any conditions when choosing which color to draw or set on a control. E.g.:
myTextField.textColor = ThemeColor.myContentTextColor ThemeColor.myCircleFillColor.setFill() NSBezierPath(rect: bounds).fill()
The text color of
myTextField
will automatically change when the user switches a theme. Similarly, the drawing code will draw with different color depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.You can also define a color to be a pattern image using
NSColor(patternImage:)
.Defining theme-aware colors
The recommended way of adding your own dynamic colors is as follows:
Add a
ThemeColor
class extension (orTKThemeColor
category on Objective-C) to add class methods for your colors. E.g.:In Swift:
extension ThemeColor { static var brandColor: ThemeColor { return ThemeColor.color(with: #function) } }
In Objective-C:
@interface TKThemeColor (Demo) + (TKThemeColor*)brandColor; @end @implementation TKThemeColor (Demo) + (TKThemeColor*)brandColor { return [TKThemeColor colorWithSelector:_cmd]; } @end
Add Class Extensions on any
Theme
you want to support (e.g.,LightTheme
andDarkTheme
-TKLightTheme
andTKDarkTheme
on Objective-C) to provide instance methods for each theme color class method defined on (1). E.g.:In Swift:
extension LightTheme { var brandColor: NSColor { return NSColor.orange } } extension DarkTheme { var brandColor: NSColor { return NSColor.white } }
In Objective-C:
@interface TKLightTheme (Demo) @end @implementation TKLightTheme (Demo) - (NSColor*)brandColor { return [NSColor orangeColor]; } @end @interface TKDarkTheme (Demo) @end @implementation TKDarkTheme (Demo) - (NSColor*)brandColor { return [NSColor whiteColor]; } @end
If supporting
UserTheme
‘s, define properties on user theme files (.theme
) for each theme color class method defined on (1). E.g.:displayName = Sample User Theme identifier = com.luckymarmot.ThemeKit.SampleUserTheme darkTheme = false brandColor = rgba(96, 240, 12, 0.5)
Overriding system colors
Besides your own colors added as
ThemeColor
class methods, you can also overrideNSColor
class methods so that they return theme-aware colors. The procedure is exactly the same, so, for example, if adding a method namedlabelColor
to aThemeColor
extension, that method will be overriden inNSColor
and the colors fromTheme
subclasses will be used instead. In sum, callingNSColor.labelColor
will return theme-aware colors.You can get the full list of available/overridable color methods (class methods) calling
NSColor.colorMethodNames()
.At any time, you can check if a system color is being overriden by current theme by checking the
NSColor.isThemeOverriden
property (e.g.,NSColor.labelColor.isThemeOverriden
).When a theme does not override a system color, the original system color will be used instead. E.g., you have overrided
ThemeColor.labelColor
, but currently applied theme does not implementlabelColor
-> originallabelColor
will be used.Fallback colors
With the exception of system overrided named colors, which defaults to the original system provided named color when theme does not specifies it, unimplemented properties/methods on target theme class will default to
fallbackForegroundColor
andfallbackBackgroundColor
, for foreground and background colors respectively. These too, can be customized per theme.Please check
See moreThemeGradient
for theme-aware gradients andThemeImage
for theme-aware images.Declaration
Swift
open class ThemeColor: NSColor
-
ThemeGradient
is aNSGradient
subclass that dynamically changes its colors whenever a new theme is make current.Theme-aware means you don’t need to check any conditions when choosing which gradient to draw. E.g.:
ThemeGradient.rainbowGradient.draw(in: bounds, angle: 0)
The drawing code will draw with different gradient depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.
Defining theme-aware gradients
The recommended way of adding your own dynamic gradients is as follows:
Add a
ThemeGradient
class extension (orTKThemeGradient
category on Objective-C) to add class methods for your gradients. E.g.:In Swift:
extension ThemeGradient { static var brandGradient: ThemeGradient { return ThemeGradient.gradient(with: #function) } }
In Objective-C:
@interface TKThemeGradient (Demo) + (TKThemeGradient*)brandGradient; @end @implementation TKThemeGradient (Demo) + (TKThemeGradient*)brandGradient { return [TKThemeGradient gradientWithSelector:_cmd]; } @end
Add Class Extensions on any
Theme
you want to support (e.g.,LightTheme
andDarkTheme
-TKLightTheme
andTKDarkTheme
on Objective-C) to provide instance methods for each theme gradient class method defined on (1). E.g.:In Swift:
extension LightTheme { var brandGradient: NSGradient { return NSGradient(starting: NSColor.white, ending: NSColor.black) } } extension DarkTheme { var brandGradient: NSGradient { return NSGradient(starting: NSColor.black, ending: NSColor.white) } }
In Objective-C:
@interface TKLightTheme (Demo) @end @implementation TKLightTheme (Demo) - (NSGradient*)brandGradient { return [[NSGradient alloc] initWithStartingColor:[NSColor whiteColor] endingColor:[NSColor blackColor]]; } @end @interface TKDarkTheme (Demo) @end @implementation TKDarkTheme (Demo) - (NSGradient*)brandGradient { return [[NSGradient alloc] initWithStartingColor:[NSColor blackColor] endingColor:[NSColor whiteColor]]; } @end
If supporting
UserTheme
‘s, define properties on user theme files (.theme
) for each theme gradient class method defined on (1). E.g.:displayName = Sample User Theme identifier = com.luckymarmot.ThemeKit.SampleUserTheme darkTheme = false orangeSky = rgb(160, 90, 45, .5) brandGradient = linear-gradient($orangeSky, rgb(200, 140, 60))
Fallback colors
Unimplemented properties/methods on target theme class will default to
fallbackGradient
. This too, can be customized per theme.Please check
See moreThemeColor
for theme-aware colors andThemeImage
for theme-aware images.Declaration
Swift
open class ThemeGradient: NSGradient
-
ThemeImage
is aNSImage
subclass that dynamically changes its colors whenever a new theme is make current.Theme-aware means you don’t need to check any conditions when choosing which image to draw. E.g.:
ThemeImage.logoImage.draw(in: bounds)
The drawing code will draw with different image depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.
Defining theme-aware images
The recommended way of adding your own dynamic images is as follows:
Add a
ThemeImage
class extension (orTKThemeImage
category on Objective-C) to add class methods for your images. E.g.:In Swift:
extension ThemeImage { static var logoImage: ThemeImage { return ThemeImage.image(with: #function) } }
In Objective-C:
@interface TKThemeImage (Demo) + (TKThemeImage*)logoImage; @end @implementation TKThemeImage (Demo) + (TKThemeImage*)logoImage { return [TKThemeImage imageWithSelector:_cmd]; } @end
Add Class Extensions on any
Theme
you want to support (e.g.,LightTheme
andDarkTheme
-TKLightTheme
andTKDarkTheme
on Objective-C) to provide instance methods for each theme image class method defined on (1). E.g.:In Swift:
extension LightTheme { var logoImage: NSImage? { return NSImage(named: "MyLightLogo") } } extension DarkTheme { var logoImage: NSImage? { return NSImage(contentsOfFile: "somewhere/MyDarkLogo.png") } }
In Objective-C:
@interface TKLightTheme (Demo) @end @implementation TKLightTheme (Demo) - (NSImage*)logoImage { return [NSImage imageNamed:@"MyLightLogo"]; } @end @interface TKDarkTheme (Demo) @end @implementation TKDarkTheme (Demo) - (NSImage*)logoImage { return [NSImage alloc] initWithContentsOfFile:@"somewhere/MyDarkLogo.png"]; } @end
If supporting
UserTheme
‘s, define properties on user theme files (.theme
) for each theme image class method defined on (1). E.g.:displayName = Sample User Theme identifier = com.luckymarmot.ThemeKit.SampleUserTheme darkTheme = false logoImage = image(named:MyLogo) //logoImage = image(file:../some/path/MyLogo.png)
Fallback images
Unimplemented properties/methods on target theme class will default to
fallbackImage
. This too, can be customized per theme.Please check
See moreThemeColor
for theme-aware colors andThemeGradient
for theme-aware gradients.Declaration
Swift
open class ThemeImage: NSImage