Theming Behaviors¶
morphui.uix.behaviors.theming
¶
BaseThemeBehavior
¶
Bases: EventDispatcher, MorphAppReferenceBehavior
Base class for theme-related behaviors.
This class serves as a common ancestor for theme-related behaviors,
providing shared functionality and properties. It is not intended
to be used directly, but rather to be extended by specific theme
behaviors such as :class:MorphColorThemeBehavior and
:class:MorphTypographyBehavior.
Key Features
- Inherits from :class:
MorphAppReferenceBehaviorto provide access to the application instance and theme manager. - Serves as a foundation for more specialized theming behaviors.
See Also
- MorphColorThemeBehavior : Provides automatic color theme integration for widgets.
- MorphTypographyBehavior : Provides typography and text styling capabilities.
- MorphAppReferenceBehavior : Provides access to app instances and MVC components.
MorphColorThemeBehavior(**kwargs)
¶
Bases: BaseThemeBehavior
Behavior that provides automatic color theme integration for MorphUI widgets.
This behavior enables widgets to automatically respond to theme changes by updating their color properties when the application theme is modified. It provides a declarative way to bind widget properties to theme colors and includes predefined style configurations for common Material Design patterns.
The behavior integrates seamlessly with other MorphUI behaviors,
particularly :class:MorphSurfaceLayerBehavior, to provide
comprehensive color theming capabilities including surface
colors, border colors, text colors, and other visual properties.
Key Features
- Automatic color updates when theme changes (light/dark mode, color scheme)
- Declarative color binding through :attr:
theme_color_bindings - Predefined Material Design style configurations
- Fine-grained control with :attr:
auto_themeproperty - Event-driven updates with :meth:
on_theme_changedcallback
Theme Integration
The behavior automatically connects to the application's
:class:ThemeManager and listens for theme change events. When
changes occur, it updates bound widget properties with the
corresponding theme colors.
Examples:
Basic usage with automatic color binding:
from morphui.uix.behaviors.theming import MorphColorThemeBehavior
from morphui.uix.behaviors.layer import MorphSurfaceLayerBehavior
from kivy.uix.label import Label
class ThemedButton(MorphColorThemeBehavior, MorphSurfaceLayerBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Bind widget properties to theme colors
self.theme_color_bindings = {
'normal_surface_color': 'primary_color',
'normal_border_color': 'outline_color',
'normal_content_color': 'content_primary_color' # text color
}
Using predefined styles:
class QuickButton(MorphColorThemeBehavior, MorphSurfaceLayerBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Apply a predefined Material Design style
self.theme_style = 'primary'
Custom theme change handling:
class AdvancedWidget(MorphColorThemeBehavior, Widget):
def on_theme_changed(self):
# Custom logic when theme changes
if self.theme_manager.theme_mode == 'Dark':
self.apply_theme_color('surface_color', 'surface_dim_color')
else:
self.apply_theme_color('surface_color', 'surface_bright_color')
Explicit property control:
class MixedThemeWidget(MorphColorThemeBehavior, MorphSurfaceLayerBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Some properties follow theme
self.theme_color_bindings = {
'normal_surface_color': 'primary_color',
'normal_content_color': 'content_primary_color',
'normal_border_color': 'outline_color'
}
# But if you pass a property explicitly in kwargs, it won't follow theme
# This is detected automatically during __init__
# Usage that automatically detects explicit properties:
widget = MixedThemeWidget(
theme_color_bindings={
'normal_surface_color': 'primary_color',
'normal_content_color': 'content_primary_color',
},
normal_surface_color=[0.2, 0.5, 0.8, 1] # Explicit - won't follow theme
)
# normal_surface_color is now in widget.explicit_color_properties
# and won't change with theme updates
# Later, allow it to follow theme again:
widget.explicit_color_properties.discard('normal_surface_color')
widget.refresh_theme_colors() # Apply theme color
See Also
- MorphSurfaceLayerBehavior : Provides surface and border styling capabilities
- MorphTypographyBehavior : Provides typography and text styling capabilities
- ThemeManager : Manages application-wide theming and color schemes
auto_theme = BooleanProperty(True)
class-attribute
instance-attribute
¶
Enable automatic theme updates for this widget.
When True, the widget automatically updates its colors when the theme changes. When False, the widget retains its current colors until manually updated.
:attr:auto_theme is a :class:~kivy.properties.BooleanProperty
and defaults to True.
theme_style = StringProperty('')
class-attribute
instance-attribute
¶
Predefined theme style to apply to this widget.
This property allows you to set a predefined Material Design style
configuration for the widget. When set to a valid style name, it
overrides any existing :attr:theme_color_bindings with the
corresponding color mappings for that style at
:attr:effective_theme_color_bindings.
This provides a quick way to style widgets according to established Material Design roles such as 'primary', 'secondary', 'tertiary', 'surface', 'error', and 'outline'. The property uses Kivy's StringProperty binding system, so changes are automatically detected and applied.
:attr:theme_style is a :class:~kivy.properties.StringProperty
and defaults to '' (no style).
theme_color_bindings = DictProperty({})
class-attribute
instance-attribute
¶
Dictionary mapping widget properties to theme color names.
This dictionary defines the automatic color binding configuration
for the widget. Each key represents a widget property name (such as
'surface_color', 'content_color', 'border_color') and each value
represents the corresponding theme color property name from the
:class:ThemeManager (such as 'primary_color', 'surface_color').
When the theme changes, widget properties listed here will be automatically updated with the corresponding theme color values.
Examples:
Basic color binding:
widget.theme_color_bindings = {
'normal_surface_color': 'primary_color',
'normal_content_color': 'content_primary_color',
'normal_border_color': 'outline_color'
}
Error state styling:
widget.theme_color_bindings = {
'normal_surface_color': 'error_color',
'normal_content_color': 'content_error_color',
'normal_border_color': 'error_color'
}
:attr:theme_color_bindings is a
:class:~kivy.properties.DictProperty and defaults to {}.
theme_style_mappings = THEME.STYLES
class-attribute
instance-attribute
¶
Predefined theme style mappings from constants.
This class attribute contains the default Material Design style configurations. Subclasses can override this to provide custom or additional style mappings.
explicit_color_properties = ObjectProperty(set())
class-attribute
instance-attribute
¶
Set of property names that are explicitly set and should not be updated by theme changes.
Properties in this set will not be automatically updated when the theme changes, preserving user-specified values. This allows fine-grained control over which properties follow the theme and which remain static.
During initialization, any color properties passed in kwargs that
match keys in :attr:theme_color_bindings or
:attr:_theme_style_color_bindings are automatically added to this
set. You can also manually add or remove property names from this
set at any time.
Examples:
Manually control which properties are explicit:
# Mark a property as explicit
widget.explicit_color_properties.add('normal_surface_color')
# Remove from explicit set to follow theme again
widget.explicit_color_properties.discard('normal_surface_color')
# Check if property is explicit
if 'normal_surface_color' in widget.explicit_color_properties:
print("This property won't follow theme changes")
:attr:explicit_color_properties is a
:class:~kivy.properties.ObjectProperty and defaults to set().
effective_color_bindings
property
¶
Get the effective color bindings after merging style and custom bindings.
This property returns the final dictionary of color bindings
that will be applied to the widget. It merges the current
:attr:theme_color_bindings with any bindings defined by the
current :attr:theme_style, giving precedence to explicit
:attr:theme_color_bindings. When both define the same widget
property, the values from :attr:theme_style have priority.
Properties in :attr:explicit_color_properties are excluded from
the effective bindings, ensuring that explicitly set colors
are not overridden by theme updates.
apply_theme_color(property_name, theme_color)
¶
Apply a specific theme color to a widget property.
This method provides manual control over theme color application, allowing you to update individual widget properties with specific theme colors outside of the automatic binding system.
The method safely handles cases where the theme color doesn't exist or the widget property is not available, returning False in such cases.
| PARAMETER | DESCRIPTION |
|---|---|
property_name
|
The name of the widget property to update. Must be a valid property on this widget instance (e.g., 'surface_color', 'content_color', 'border_color').
TYPE:
|
theme_color
|
The name of the theme color property to use. Must be a valid color property on the ThemeManager (e.g., 'primary_color', 'surface_color', 'on_primary_color').
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
True if the color was successfully applied, False if either the theme color doesn't exist, the widget property doesn't exist, or the theme color value is None. |
Examples:
Apply primary color to surface:
success = widget.apply_theme_color('surface_color', 'primary_color')
if success:
print("Color applied successfully")
Conditional color application:
add_custom_style(style_name, color_mappings)
¶
Add a custom theme style to the available styles.
This method allows you to define new theme styles that can be
used by setting the :attr:theme_style property. Custom styles
are added to the instance's :attr:theme_style_mappings and can
be used immediately.
If a style with the same name already exists, it will be overwritten with the new color mappings. This allows you to customize or update existing styles as needed.
| PARAMETER | DESCRIPTION |
|---|---|
style_name
|
The name for the new custom style.
TYPE:
|
color_mappings
|
Dictionary mapping widget properties to theme color names,
same format as :attr:
TYPE:
|
Examples:
Add a custom warning style:
widget.add_custom_style('warning', {
'normal_surface_color': 'error_container_color',
'normal_content_color': 'content_error_container_color',
'normal_border_color': 'outline_color'
})
# Now use the custom style
widget.theme_style = 'warning'
Add a subtle style:
widget.add_custom_style('subtle', {
'normal_surface_color': 'surface_variant_color',
'normal_content_color': 'content_surface_variant_color',
'normal_border_color': 'outline_variant_color'
})
Notes
If this is the first custom style being added to the instance,
the method creates a copy of the class-level theme_style_
mappings. This ensures that modifications to the instance's
style mappings do not affect other instances or the class.
If you want to modify the class-level mappings for all
instances, you can do so by directly modifying the
:attr:theme_style_mappings class attribute.
refresh_theme_colors()
¶
Manually refresh all theme colors.
This method forces an update of all bound theme colors, useful when you want to ensure colors are up to date.
Notes
The explicit set of properties in
:attr:explicit_color_properties is respected during this
refresh, so any properties marked as explicit will not be
updated by this method. This allows you to refresh theme colors
while preserving any manually set values that should not follow
the theme.
on_colors_updated(*args)
¶
Event callback fired after theme colors are updated within the theme manager but before they are applied to the widget.
This can be used to perform actions or adjustments based on the new color values before they are applied to the widget's properties.
Override this method in subclasses to implement custom behavior when theme colors are updated.
MorphTypographyBehavior(**kwargs)
¶
Bases: BaseThemeBehavior
Behavior that provides automatic typography integration for MorphUI widgets.
This behavior enables widgets to automatically apply Material Design typography styles and respond to typography system changes. It provides a declarative way to set typography roles, sizes, and weights while maintaining consistency with the application's typography system.
Key Features
- Automatic typography updates when app font family changes
- Material Design typography role system (Display, Headline, Title, Body, Label)
- Typography size variants (large, medium, small)
- Font weight control (Thin, Regular, Heavy)
- Fine-grained control with :attr:
auto_typographyproperty - Event-driven updates with :meth:
on_typography_changedcallback
Typography Integration
The behavior automatically connects to the application's
:class:Typography system and listens for typography change events.
When changes occur, it updates the widget's typography properties
according to the current role, size, and weight settings.
Examples:
Basic usage with typography role:
from morphui.uix.behaviors.theming import MorphTypographyBehavior
from kivy.uix.label import Label
class TypedLabel(MorphTypographyBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.typography_role = 'Headline'
self.typography_size = 'large'
Manual typography application:
class CustomWidget(MorphTypographyBehavior, Widget):
def setup_typography(self):
self.apply_typography_style(
role='Body',
size='medium',
font_weight='Regular'
)
See Also
MorphColorThemeBehavior : Provides color theme integration Typography : Manages application-wide typography styles
typography_role = OptionProperty('Label', options=['Display', 'Headline', 'Title', 'Body', 'Label'])
class-attribute
instance-attribute
¶
Typography role for automatic text styling.
Sets the Material Design typography role which automatically configures appropriate font family, size, and line height. Available roles: 'Display', 'Headline', 'Title', 'Body', 'Label'.
When set, the widget automatically applies the corresponding
typography style based on the current :attr:typography_size and
app font settings.
:attr:typography_role is a :class:~kivy.properties.OptionProperty
and defaults to 'Label'.
typography_size = OptionProperty('medium', options=['large', 'medium', 'small'])
class-attribute
instance-attribute
¶
Size variant for the typography role.
Available options: 'large', 'medium', 'small'
Works in conjunction with :attr:typography_role to determine
the final text styling.
:attr:typography_size is a
:class:~kivy.properties.OptionProperty and defaults to 'medium'.
typography_weight = OptionProperty('Regular', options=['Thin', 'Regular', 'Heavy'])
class-attribute
instance-attribute
¶
Weight variant for the typography role.
Available options: 'Thin', 'Regular', 'Heavy'
Works in conjunction with :attr:typography_role to determine
the final text styling.
:attr:typography_weight is a
:class:~kivy.properties.OptionProperty and defaults to 'Regular'.
auto_typography = BooleanProperty(True)
class-attribute
instance-attribute
¶
Enable automatic typography updates for this widget.
When True, the widget automatically updates its typography when the app font family changes or when typography properties are modified.
:attr:auto_typography is a
:class:~kivy.properties.BooleanProperty and defaults to True.
apply_typography_style(font_name, role, size, font_weight='Regular')
¶
Apply typography style to this widget.
This method applies the specified typography style to the widget
based on the provided role, size, and font weight. It retrieves
the appropriate text style from the :attr:typography system and
updates the widget's font properties accordingly.
| PARAMETER | DESCRIPTION |
|---|---|
font_name
|
Optional font name to override the default font family from the typography system. If None, uses the default font family defined in the typography settings.
TYPE:
|
role
|
Typography role ('Display', 'Headline', 'Title', 'Body', 'Label')
TYPE:
|
size
|
Size variant ('large', 'medium', 'small')
TYPE:
|
font_weight
|
Font weight ('Thin', 'Regular', 'Heavy'), defaults to 'Regular'
TYPE:
|
refresh_typography()
¶
Manually refresh typography style.
This method forces an update of the typography style, useful when you want to ensure typography is up to date.
on_typography_updated(*args)
¶
Called after typography is applied to the widget.
Override this method in subclasses to implement custom behavior when typography is updated.
MorphThemeBehavior(**kwargs)
¶
Bases: MorphColorThemeBehavior, MorphTypographyBehavior
Combined behavior providing both color theming and typography integration.
This behavior combines :class:MorphColorThemeBehavior and
:class:MorphTypographyBehavior to provide comprehensive theming
capabilities including automatic color updates, typography styling,
and theme integration.
This is a convenience class that provides the same functionality as the original MorphThemeBehavior while allowing users to choose between the combined behavior or individual specialized behaviors.
For new code, consider using the individual behaviors (:class:MorphColorThemeBehavior
and :class:MorphTypographyBehavior) for better modularity and clearer separation
of concerns.
Examples:
Using the combined behavior:
from morphui.uix.behaviors.layer import MorphSurfaceLayerBehavior
from morphui.uix.behaviors.theming import MorphThemeBehavior
from kivy.uix.label import Label
class FullyThemedLabel(MorphThemeBehavior, MorphSurfaceLayerBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_style = 'primary'
self.typography_role = 'Headline'
self.typography_size = 'large'
See Also
MorphColorThemeBehavior : Provides color theme integration only MorphTypographyBehavior : Provides typography integration only
MorphDelegatedThemeBehavior(**kwargs)
¶
Bases: EventDispatcher
Behavior that allows a container widget to delegate theme properties to its child widgets.
This behavior is designed for container widgets that hold other themed widgets. It enables the container to manage and propagate theme colors to its children, ensuring consistent theming across all contained widgets.
Examples:
Using the delegated theme behavior in a container:
from morphui.uix.behaviors.theming import MorphDelegatedThemeBehavior
from morphui.uix.boxlayout import MorphBoxLayout
class ThemedContainer(
MorphDelegatedThemeBehavior,
MorphBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Child widgets will inherit theme properties from this container
Notes
This behavior is intended to be used in conjunction with container
widgets that hold other themed widgets. It ensures that all child
widgets receive consistent theming based on the container's theme
settings. Ensure that the subclassed container widget also
implements the necessary behaviors:
- :class:MorphColorThemeBehavior for color theming
- :class:MorphContentLayerBehavior for content color theming
delegate_content_color = BooleanProperty(True)
class-attribute
instance-attribute
¶
Whether to delegate content color theming to child widgets.
When True, the container will manage the content color of its children, ensuring consistent text and icon colors. When False, children will manage their own content colors independently.
:attr:delegate_content_color is a
:class:~kivy.properties.BooleanProperty and defaults to True.
delegated_children = ListProperty([])
class-attribute
instance-attribute
¶
List of child widgets to which theme delegation should be applied.
This property allows you to specify which child widgets should have their theme properties delegated by the container. If the list is empty (default), delegation applies to all children. If the list contains specific widget instances, only those widgets will have their theme properties managed by the container.
:attr:delegated_children is a
:class:~kivy.properties.ListProperty and defaults to []
(all children).
Examples:
Delegate to all children (default):
Delegate to specific children only: