Feathers UI
  • Docs
  • API
  • Showcase
  • Blog
  • Community

›Custom UI Components

Introduction

  • Getting Started
  • Installation
  • Create a new project in…

    • Command line terminal
    • HaxeDevelop
    • Moonshine IDE
    • Visual Studio Code

    Build a project targeting…

    • Android
    • Electron
    • HashLink VM
    • iOS
    • Linux
    • macOS
    • Neko VM
    • Web
    • Windows
  • Introduction to OpenFL

UI Components

  • Intro to UI components
  • Alert
  • Application
  • AssetLoader
  • Button
  • ButtonBar
  • Callout
  • Check
  • ComboBox
  • DatePicker
  • HDividedBox / VDividedBox
  • Drawer
  • Form / FormItem
  • GridView
  • GroupListView
  • Header
  • HierarchicalItemRenderer
  • ItemRenderer
  • Label
  • LayoutGroup
  • LayoutGroupItemRenderer
  • ListView
  • NumericStepper
  • PageIndicator
  • PageNavigator
  • Panel
  • PopUpDatePicker
  • PopUpListView
  • HProgressBar / VProgressBar
  • Radio
  • RouterNavigator
  • HScrollBar / VScrollBar
  • ScrollContainer
  • HSlider / VSlider
  • StackNavigator
  • TabBar
  • TabNavigator
  • TextArea
  • TextCallout
  • TextInput
  • ToggleButton
  • ToggleSwitch
  • TreeGridView
  • TreeView

Layouts

  • Intro to Layouts
  • AnchorLayout
  • FlowRowsLayout
  • HorizontalLayout
  • HorizontalListLayout
  • HorizontalDistributedLayout
  • ResponsiveGridLayout
  • TiledRowsLayout
  • TiledRowsListLayout
  • PagedTiledRowsListLayout
  • VerticalLayout
  • VerticalListFixedRowLayout
  • VerticalListLayout
  • VerticalDistributedLayout
  • Layout Data
  • Custom layouts

Styles and Skins

  • Intro to skins
  • Common shape skins
  • Custom programmatic skins
  • Intro to themes
  • Create a custom theme

Animation

  • Navigator animated transitions
  • Custom navigator transitions

Custom UI Components

  • Custom UI components
  • Component lifecycle
  • Custom item renderers
  • Class-based item renderers

Miscellaneous

  • CLI commands
  • Collections
  • Focus management
  • haxedef options
  • Displaying pop-ups
  • Semantic versioning
  • Tool-tips
  • Cookbook
  • Install Prerelease Builds
Edit

Create custom UI components for Feathers UI

🚧 Under construction! This documentation is still being written.

Feathers UI includes dozens of built-in UI components, but sometimes, a project will need a specialized component that isn't included in the framework by default. Developers may fill in these gaps by creating their own, custom UI components from scratch by extending the appropriate class and hooking into the available APIs.

At the heart of all UI components is the FeathersControl class. This abstract base class provides much of the core functionality shared by most UI components in the framework, including hooks for validation, styling, measurement, and focus. The most advanced custom UI components will generally extend this class.

Some custom UI components don't require much integration with low-level API hooks in the framework. For instance, a particular custom component might simply need to display several other components as children in a layout. In those cases, it may make sense to extend one of the simple containers, like LayoutGroup, ScrollContainer, or Panel, instead of FeathersControl. Many of the techniques below still apply to container subclasses, but you may be able to skip certain things, like manually measuring children and laying them out.

A basic FeathersControl subclass template

A typical subclass of FeathersControl will include a constructor, an override of initialize(), and an override of update().

class MyComponent extends FeathersControl {
    public function new() {
        super();
    }

    override private function initialize():Void {
        super.initialize();
    }

    override private function update():Void {
        super.update();
    }
}

See UI component lifecycle for more information about these methods and other methods that are called during the lifetime of a UI component instance.

Adding children

Typically, children are added in override of the initialize() method.

private var okButton:Button;

override private function initialize():Void {
    super.initialize();

    if (okButton == null) {
        okButton = new Button();
        okButton.text = "OK";
        addChild(okButton);
    }
}

When creating an override of initialize(), always remember to call super.initialize(). The superclass may also have important code that needs to run to work properly.

Before creating a child, it's often a good practice to check whether it might have already been created. Another developer (or you in the future!) may need to create a subclass of the component, and this allows them to customize the children.

Updating after property changes

Many UI components expose properties that allow developers to customize the component's appearance and behavior. Generally, it's a good idea to assume that developers might change multiple properties at once. To improve performance, a component should wait until all property changes are done before updating itself. A FeathersControl can queue an update for later by calling setInvalid(). Then, it can process all updates together in an override of update().

public var okButtonText(default, set):String = "OK";

private function set_okButtonText(value:String):String {
  if (okButtonText == value) {
      return okButtonText;
  }
  okButtonText = value;
  setInvalid(DATA);
  return okButtonText;
}
override private function update():Void {
    var dataInvalid = isInvalid(DATA);

    if (dataInvalid) {
      okButton.text = okButtonText;
    }

    super.update();
}

Styles

To allow a custom UI component to be styled by a theme, add @:styleContext metadata to the class.

@:styleContext
class MyComponent extends FeathersControl {}
@:style
public var horizontalAlign:TextFormat = CENTER;

A property with @:style metadata must be public and it must be initialized with a default value. This allows the component to easily reset its styles back to their defaults, such as when the theme changes.

When using @:style metadata, a setter function is automatically generated, and one of the things this function does is call setInvalid(STYLES). You can process this invalidation flag in an override of update()

override private function update():Void {
    var stylesInvalid = isInvalid(STYLES);

    if (stylesInvalid) {
      // handle style changes
    }

    super.update();
}

Related Links

  • UI component lifecycle
  • Create custom item renderers for data containers
Last updated on 7/25/2022
← Custom navigator transitionsComponent lifecycle →
  • A basic FeathersControl subclass template
  • Adding children
  • Updating after property changes
  • Styles
  • Related Links
Feathers UI
Feathers UI
  • Downloads
  • Showcase
  • Testimonials
  • Premium Support
Documentation
  • Getting Started
  • API Reference
  • Samples
    Github
  • Source Code
  • Issue Tracker
Community
  • Forum
  • Discord
  • Stack Overflow
News & Updates
  • Blog (RSS, Atom)
  • Twitter
Make a Donation
  • Join Github Sponsors
  • Donate with PayPal
  • Buy a T-Shirt
Copyright © 2022 Bowler Hat LLC — Illustrations by unDraw.