Skip to content

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:MorphAppReferenceBehavior to 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_theme property
  • Event-driven updates with :meth:on_theme_changed callback
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: str

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: str

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:

if widget.theme_manager.theme_mode == 'Dark':
    widget.apply_theme_color('surface_color', 'surface_dim_color')
else:
    widget.apply_theme_color('surface_color', 'surface_bright_color')

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: str

color_mappings

Dictionary mapping widget properties to theme color names, same format as :attr:theme_color_bindings.

TYPE: Dict[str, str]

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_typography property
  • Event-driven updates with :meth:on_typography_changed callback
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: str | None

role

Typography role ('Display', 'Headline', 'Title', 'Body', 'Label')

TYPE: str

size

Size variant ('large', 'medium', 'small')

TYPE: str

font_weight

Font weight ('Thin', 'Regular', 'Heavy'), defaults to 'Regular'

TYPE: str DEFAULT: 'Regular'

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):

container.delegated_children = []

Delegate to specific children only:

label1 = Label()
label2 = Label()
button = Button()
container.add_widget(label1)
container.add_widget(label2)
container.add_widget(button)

# Only delegate to label1 and label2, not button
container.delegated_children = [label1, label2]