ml::docs | Your First App Manifests Events Plugins Scenes Graphics Resources
← All Tutorials
Beginner 4 sections ~10 min C++17/20

Flags vs States

Understanding when to use flags versus states is the key to writing expressive, maintainable Malena components.

Introduction

Flags vs States — The Core Difference

These two systems work together but serve different purposes. Knowing which to use makes your components cleaner and more expressive.

Flags — Independent Booleans

  • Each flag is independent — any combination can be true at once
  • No mutual exclusivity between flags
  • No transition callbacks
  • Best for: properties that can coexist (selected AND highlighted AND dragging)
  • Read in draw() to adjust visuals

States — Exclusive Active Mode

  • Only one state is active at a time
  • Transitions fire onStateEnter / onStateExit
  • Models mutually exclusive modes
  • Best for: modes where behavior changes meaningfully (Idle, Active, Dead)
  • Drive state-specific animation in callbacks

1Core Concepts

When to Use Each

1

Can multiple properties be true simultaneously?

Yes → use flags. A button can be hovered AND selected AND highlighted at the same time. These are independent booleans.

2

Do properties exclude each other?

Yes → use a state. A character can only be Idle OR Running OR Jumping, never two at once. States enforce this mutual exclusivity automatically.

3

Do you need transition callbacks?

Yes → use states. onStateEnter / onStateExit let you react to transitions cleanly, without polling. Flags have no such callbacks.

4

Is it a simple binary on/off property?

Yes → use a flag. Draggable, hidden, enabled, completed — simple boolean properties that don't transition into each other.


2Core Concepts

Quick Mental Model

Flags = adjectives that can all apply at once (selected, highlighted, pinned, completed)

States = modes that exclude each other (idle, active, paused, dead)

If you could say "the card is selected and highlighted and pinned" → flags.
If you'd say "the enemy is either idle or chasing or attacking" → state.
Correct usage — flags and states together cpp
class TaskCard : public ml::ComponentWith
{
    // Manifest declares:
    // enum class Flag  { Selected, Highlighted, Completed, Pinned };
    // enum class State { Idle, Active, Done };
    //
    // Flag::Selected/Highlighted/Completed can ALL be true simultaneously ✓
    // State::Idle/Active/Done are mutually exclusive ✓

public:
    void registerEvents()
    {
        // State machine: entering Done also sets Completed flag
        onStateEnter([this](State s) {
            if (s == State::Done) {
                enableFlag(Flag::Completed);  // flag set alongside state transition
                fadeTo(200, 0.3f);            // visual change driven by state
            }
        });

        // Flags: hover and selected can combine freely
        onHover([this]    { enableFlag(Flag::Highlighted); });
        onUnhover([this]  { disableFlag(Flag::Highlighted); });

        onClick([this]{
            if (isState(State::Done))
                setState(State::Active);   // ← state machine transition
            else
                setState(State::Done);
        });
    }

    void draw(sf::RenderTarget& t, sf::RenderStates s) const override
    {
        // State drives exclusive color
        sf::Color bg;
        switch (getState()) {
            case State::Active: bg = sf::Color(50, 90, 50);  break;
            case State::Done:   bg = sf::Color(30, 30, 30);  break;
            default:            bg = sf::Color(38, 33, 92);  break;
        }
        _body.setFillColor(bg);

        // Flags drive overlay effects — can combine freely
        if (checkFlag(Flag::Highlighted))
            _body.setOutlineColor(sf::Color(255, 255, 255, 80));
        if (checkFlag(ml::Flag::DRAGGING))
            _body.setOutlineColor(sf::Color(93, 202, 165, 200));

        t.draw(_body, s);
    }
};

3Core Concepts

Common Mistakes to Avoid

Anti-pattern: booleans instead of a state cpp
// ❌ Three flags for mutually exclusive modes — can accidentally combine
class Enemy {
    bool _isIdle      = true;
    bool _isChasing   = false;
    bool _isAttacking = false;
    // Bug: both _isIdle and _isAttacking can be true at once
};

// ✅ State machine enforces mutual exclusivity
class Enemy : public ml::ComponentWith {
    // enum class State { Idle, Chasing, Attacking };
    // setState() makes impossible combinations impossible
};
Anti-pattern: state for independent properties cpp
// ❌ Combinatorial explosion — you'd need HoveredAndSelected, etc.
enum class BadState { Normal, Hovered, Selected, HoveredAndSelected, Disabled ... };

// ✅ Independent flags — any combination is naturally expressible
enum class Flag { Hovered, Selected, Disabled };
// Component can be Selected AND Disabled AND Hovered simultaneously