# FlexLöve Examples

## 🎨 Flexbox & Grid Layouts

FlexLöve provides a powerful CSS-like layout system using flexbox and grid positioning. Build complex responsive UIs with nested containers and automatic layout calculations.

### Basic Flex Layout

    -- Create a main window with vertical flex layout
    local window = FlexLove.new({
      x = "10%",
      y = "10%",
      width = "80%",
      height = "80%",
      themeComponent = "framev3",
      positioning = "flex",
      flexDirection = "vertical",
      gap = 20,
      padding = { horizontal = 20, vertical = 20 }
    })

    -- Add a title
    FlexLove.new({
      parent = window,
      text = "Advanced Layout Example",
      textAlign = "center",
      textSize = "3xl",
      width = "100%"
    })

### True Grid Layout

    -- Create a true grid with positioning = "grid"
    local gridContainer = FlexLove.new({
      parent = window,
      positioning = "grid",
      gridRows = 3,
      gridColumns = 3,
      columnGap = 5,
      rowGap = 5,
      height = "80%",
      alignItems = "stretch"
    })

    -- Add grid items (auto-flow into cells)
    for i = 1, 9 do
      FlexLove.new({
        parent = gridContainer,
        themeComponent = "buttonv2",
        text = "Cell " .. i,
        textAlign = "center",
        textSize = "md",
        onEvent = function(_, event)
          if event.type == "release" then
            print("Grid cell " .. i .. " clicked")
          end
        end
      })
    end

### Grid with Headers (Schedule-style)

    local Color = FlexLove.Color
    local Theme = FlexLove.Theme

    -- Example data
    local columnHeaders = { "Mon", "Tue", "Wed" }
    local rowHeaders = { "Task A", "Task B", "Task C" }

    -- Calculate grid dimensions: +1 for header row and column
    local numRows = #rowHeaders + 1
    local numColumns = #columnHeaders + 1

    local scheduleGrid = FlexLove.new({
      parent = window,
      positioning = "grid",
      gridRows = numRows,
      gridColumns = numColumns,
      columnGap = 2,
      rowGap = 2,
      height = "80%",
      alignItems = "stretch"
    })

    local accentColor = Theme.getColor("primary")
    local textColor = Theme.getColor("text")

    -- Top-left corner cell (empty)
    FlexLove.new({ parent = scheduleGrid })

    -- Column headers
    for _, header in ipairs(columnHeaders) do
      FlexLove.new({
        parent = scheduleGrid,
        text = header,
        textColor = textColor,
        textAlign = "center",
        backgroundColor = Color.new(0, 0, 0, 0.3),
        border = { top = true, right = true, bottom = true, left = true },
        borderColor = accentColor,
        textSize = 12
      })
    end

    -- Data rows
    for i, rowHeader in ipairs(rowHeaders) do
      -- Row header
      FlexLove.new({
        parent = scheduleGrid,
        text = rowHeader,
        backgroundColor = Color.new(0, 0, 0, 0.3),
        textColor = textColor,
        textAlign = "center",
        border = { top = true, right = true, bottom = true, left = true },
        borderColor = accentColor,
        textSize = 10
      })

      -- Data cells
      for j = 1, #columnHeaders do
        FlexLove.new({
          parent = scheduleGrid,
          text = tostring((i * j) % 5),
          textAlign = "center",
          border = { top = true, right = true, bottom = true, left = true },
          borderColor = Color.new(0.5, 0.5, 0.5, 1.0),
          textSize = 12,
          themeComponent = "buttonv2",
          onEvent = function(elem, event)
            if event.type == "click" then
              local newValue = (tonumber(elem.text) + 1) % 10
              elem:updateText(tostring(newValue))
            end
          end
        })
      end
    end

💡 Tip: Use `positioning = "grid"` with `gridRows` and `gridColumns` to create true grid layouts. Children auto-flow into cells left-to-right, top-to-bottom. Perfect for tables, schedules, and structured data displays.

View Complete Example →

## 🎭 Theme System

FlexLöve includes a powerful 9-patch NinePatch theming system with automatic state management (normal, hover, pressed, disabled).

### Initialize with Theme

    local FlexLove = require("FlexLove")

    -- Initialize with a built-in theme
    FlexLove.init({
      baseScale = { width = 1920, height = 1080 },
      theme = "space"  -- Options: "space", "metal"
    })

### Using Theme Components

    -- Create a themed button (automatic state handling)
    local button = FlexLove.new({
      themeComponent = "buttonv2",
      text = "Themed Button",
      width = "20vw",
      height = "10vh",
      textAlign = "center",
      onEvent = function(elem, event)
        if event.type == "release" then
          print("Button clicked!")
        end
      end
    })

    -- Available theme components:
    -- "buttonv1", "buttonv2", "buttonv3"
    -- "framev1", "framev2", "framev3"
    -- "inputv1", "inputv2"
    -- "cardv1", "cardv2"
    -- "panel"

### Theme Switching

    local themes = { "space", "metal" }
    local themeIndex = 1

    -- Create theme toggle button
    FlexLove.new({
      themeComponent = "buttonv2",
      text = "Switch Theme",
      onEvent = function(_, event)
        if event.type == "release" then
          themeIndex = (themeIndex % #themes) + 1
          FlexLove.setTheme(themes[themeIndex])
          print("Switched to:", themes[themeIndex])
        end
      end
    })

💡 Tip: Theme components automatically handle hover, pressed, and disabled states using 9-patch image scaling for perfect corners at any size.

View Complete Example →

## ✨ State Management

Build interactive UIs with state tracking, counters, toggles, and dynamic updates. Perfect for game menus and settings screens.

### Counter with State

    local state = {
      counter = 0,
      isToggled = false,
      inputValue = ""
    }

    -- Create counter display
    local counterSection = FlexLove.new({
      positioning = "flex",
      flexDirection = "horizontal",
      justifyContent = "space-between",
      alignItems = "center",
      gap = 10
    })

    local counterText = FlexLove.new({
      parent = counterSection,
      text = "Counter: " .. state.counter,
      textSize = "lg"
    })

    -- Increment button
    FlexLove.new({
      parent = counterSection,
      themeComponent = "buttonv2",
      text = "Increment",
      width = "25%",
      onEvent = function(_, event)
        if event.type == "release" then
          state.counter = state.counter + 1
          counterText.text = "Counter: " .. state.counter
          print("Counter incremented to:", state.counter)
        end
      end
    })

### Toggle Switch

    -- Create toggle button with color change
    local toggleButton = FlexLove.new({
      positioning = "flex",
      justifyContent = "center",
      alignItems = "center",
      width = 60,
      height = 30,
      backgroundColor = state.isToggled and "#48bb78" or "#a0aec0",
      borderRadius = 15,
      padding = { horizontal = 5 },
      onEvent = function(elem, event)
        if event.type == "release" then
          state.isToggled = not state.isToggled
          -- Update button color
          elem.backgroundColor = state.isToggled and "#48bb78" or "#a0aec0"
          print("Toggle switched to:", state.isToggled)
        end
      end
    })

    FlexLove.new({
      parent = toggleButton,
      text = state.isToggled and "ON" or "OFF",
      textAlign = "center",
      textSize = "sm",
      color = "#ffffff"
    })

### Text Input with State

    -- Input field that updates state
    local inputField = FlexLove.new({
      themeComponent = "inputv2",
      text = state.inputValue,
      textAlign = "left",
      textSize = "md",
      width = "50%",
      onEvent = function(_, event)
        if event.type == "textinput" then
          state.inputValue = state.inputValue .. event.text
          print("Input value:", state.inputValue)
        elseif event.type == "keypressed" and event.key == "backspace" then
          state.inputValue = state.inputValue:sub(1, -2)
        end
      end
    })

💡 Tip: Use local state tables to manage UI state and update element properties directly when state changes.

View Complete Example →

## 📜 Scrollable Content

Create smooth scrolling containers with backdrop blur effects and overflow handling for long content lists.

### Scrollable Container

    local Color = FlexLove.Color

    -- Create main window with backdrop blur
    local window = FlexLove.new({
      x = "25%",
      y = "10%",
      width = "50vw",
      height = "80vh",
      themeComponent = "framev3",
      positioning = "flex",
      flexDirection = "vertical",
      gap = 10,
      backdropBlur = { intensity = 50, quality = 10 },
      backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8)
    })

    -- Create scroll container
    local scrollContainer = FlexLove.new({
      parent = window,
      width = "90%",
      height = "70%",
      positioning = "flex",
      flexDirection = "vertical",
      overflowY = "scroll",  -- Enable vertical scrolling
      gap = 5,
      padding = { horizontal = 10, vertical = 5 },
      themeComponent = "framev3",
      backgroundColor = Color.new(0.2, 0.2, 0.2, 0.5)
    })

### Add Scrollable Items

    -- Add multiple items to demonstrate scrolling
    for i = 1, 30 do
      local text = string.format(
        "Item %d - This is scrollable content that exceeds the container bounds",
        i
      )

      FlexLove.new({
        parent = scrollContainer,
        text = text,
        textAlign = "start",
        textSize = "md",
        width = "100%",
        textColor = Color.new(0.9, 0.9, 0.9, 1),
        padding = { vertical = 5 },
        themeComponent = i % 3 == 0 and "panel" or "cardv2",
        backgroundColor = i % 3 == 0
          and Color.new(0.3, 0.3, 0.3, 0.7)
          or Color.new(0.4, 0.4, 0.4, 0.5)
      })
    end

    -- Footer with instructions
    FlexLove.new({
      parent = window,
      text = "Scroll using the mouse wheel or drag the scrollbar",
      textAlign = "center",
      textSize = "sm",
      textColor = Color.new(0.7, 0.7, 0.7, 1)
    })

💡 Tip: Use `overflowY = "scroll"` to enable vertical scrolling. The backdrop blur creates a nice glassmorphism effect behind the container.

View Complete Example →

## 🎚️ Sliders & Controls

Implement draggable sliders with value tracking, perfect for settings menus and adjustable parameters.

### Create a Slider Control

    local function createSlider(parent, label, min, max, initialValue)
      local value = initialValue or min
      local normalized = (value - min) / (max - min)

      local row = FlexLove.new({
        parent = parent,
        width = "100%",
        height = "5vh",
        positioning = "flex",
        flexDirection = "horizontal",
        justifyContent = "space-between",
        alignItems = "center",
        gap = 10
      })

      -- Label
      FlexLove.new({
        parent = row,
        text = label,
        textAlign = "start",
        textSize = "md",
        width = "30%"
      })

      local sliderContainer = FlexLove.new({
        parent = row,
        width = "50%",
        height = "100%",
        positioning = "flex",
        flexDirection = "horizontal",
        alignItems = "center",
        gap = 5
      })

      -- Slider track
      local sliderTrack = FlexLove.new({
        parent = sliderContainer,
        width = "80%",
        height = "75%",
        positioning = "flex",
        flexDirection = "horizontal",
        themeComponent = "framev3"
      })

      -- Fill bar (visual indicator)
      local fillBar = FlexLove.new({
        parent = sliderTrack,
        width = (normalized * 100) .. "%",
        height = "100%",
        themeComponent = "buttonv1"
      })

      -- Value display
      local valueDisplay = FlexLove.new({
        parent = sliderContainer,
        text = string.format("%d", value),
        textAlign = "center",
        textSize = "md",
        width = "15%"
      })

      -- Handle drag events
      local function updateValue(mx)
        local normalized = (mx - sliderTrack.x) / sliderTrack.width
        normalized = math.max(0, math.min(1, normalized))
        value = min + (normalized * (max - min))

        -- Update visuals
        fillBar.width = (normalized * 100) .. "%"
        valueDisplay.text = string.format("%d", value)
      end

      sliderTrack.onEvent = function(elem, event)
        if event.type == "press" or event.type == "drag" then
          updateValue(event.x)
        end
      end

      return value
    end

### Using Multiple Sliders

    -- Create settings window
    local settingsWindow = FlexLove.new({
      x = "10%",
      y = "10%",
      width = "80%",
      height = "80%",
      themeComponent = "framev3",
      positioning = "flex",
      flexDirection = "vertical",
      gap = 20,
      padding = { horizontal = "5%", vertical = "3%" }
    })

    -- Title
    FlexLove.new({
      parent = settingsWindow,
      text = "Settings",
      textAlign = "center",
      textSize = "3xl"
    })

    -- Create sliders for different settings
    createSlider(settingsWindow, "Volume", 0, 100, 75)
    createSlider(settingsWindow, "Brightness", 0, 100, 50)
    createSlider(settingsWindow, "Sensitivity", 10, 200, 100)

💡 Tip: Use `event.type == "drag"` to track mouse movement while pressed for smooth slider interaction.

View Complete Example →

## ⌨️ Input & Event Handling

Handle mouse, keyboard, and touch events with FlexLöve's comprehensive event system. Includes focus management and input field support.

### Mouse Event Handling

    local inputState = {
      mousePos = { x = 0, y = 0 },
      isHovered = false,
      hoverCount = 0
    }

    -- Create hoverable area
    local hoverArea = FlexLove.new({
      width = "30%",
      height = "100%",
      backgroundColor = "#4a5568",
      borderRadius = 8,
      onEvent = function(elem, event)
        if event.type == "mousemoved" then
          inputState.mousePos.x = event.x
          inputState.mousePos.y = event.y
        elseif event.type == "mouseenter" then
          inputState.isHovered = true
          inputState.hoverCount = inputState.hoverCount + 1
          elem.backgroundColor = "#48bb78"  -- Green on hover
        elseif event.type == "mouseleave" then
          inputState.isHovered = false
          elem.backgroundColor = "#4a5568"  -- Back to gray
        elseif event.type == "release" then
          print("Clicked at:", event.x, event.y)
        end
      end
    })

    -- Display mouse position
    FlexLove.new({
      text = "Mouse: (" .. inputState.mousePos.x .. ", " .. inputState.mousePos.y .. ")",
      textAlign = "left"
    })

### Keyboard Input

    local keyState = {
      lastKey = "",
      textInput = ""
    }

    -- Input field with keyboard handling
    local inputField = FlexLove.new({
      themeComponent = "inputv2",
      text = keyState.textInput,
      width = "50%",
      onEvent = function(elem, event)
        if event.type == "textinput" then
          keyState.textInput = keyState.textInput .. event.text
          keyState.lastKey = event.text
          elem.text = keyState.textInput
        elseif event.type == "keypressed" then
          keyState.lastKey = event.key

          if event.key == "backspace" then
            keyState.textInput = keyState.textInput:sub(1, -2)
            elem.text = keyState.textInput
          elseif event.key == "return" then
            print("Submitted:", keyState.textInput)
            keyState.textInput = ""
            elem.text = ""
          elseif event.key == "escape" then
            elem:blur()  -- Remove focus
          end
        end
      end
    })

### Touch Events (Mobile Support)

    local touchState = {
      touchPos = { x = 0, y = 0 },
      isTouching = false
    }

    -- Touchable area
    local touchArea = FlexLove.new({
      width = "100%",
      height = "100%",
      backgroundColor = "#4a5568",
      borderRadius = 8,
      onEvent = function(_, event)
        if event.type == "touch" then
          touchState.isTouching = true
          touchState.touchPos.x = event.x
          touchState.touchPos.y = event.y
          print("Touch started at:", event.x, event.y)
        elseif event.type == "touchmoved" then
          touchState.touchPos.x = event.x
          touchState.touchPos.y = event.y
        elseif event.type == "touchreleased" then
          touchState.isTouching = false
          print("Touch ended")
        end
      end
    })

### Available Event Types

    -- Mouse events
    "press"         -- Mouse button pressed
    "release"       -- Mouse button released
    "drag"          -- Mouse moved while pressed
    "mousemoved"    -- Mouse position changed
    "mouseenter"    -- Mouse entered element bounds
    "mouseleave"    -- Mouse left element bounds
    "wheel"         -- Mouse wheel scrolled

    -- Keyboard events
    "keypressed"    -- Key pressed (includes key name)
    "keyreleased"   -- Key released
    "textinput"     -- Text character entered

    -- Touch events (mobile)
    "touch"         -- Touch started
    "touchmoved"    -- Touch position changed
    "touchreleased" -- Touch ended

    -- Focus events
    "focus"         -- Element gained focus
    "blur"          -- Element lost focus

💡 Tip: Use `elem:focus()` and `elem:blur()` to programmatically control input field focus. The event object contains contextual data like `x`, `y`, `key`, etc.

View Complete Example →

## ⚡ Performance Tips

Best practices for optimizing FlexLöve applications and avoiding common performance pitfalls.

### Immediate Mode vs Retained Mode

    -- Retained Mode (default) - elements persist between frames
    FlexLove.init({
      baseScale = { width = 1920, height = 1080 }
    })

    -- Immediate Mode - elements recreated each frame
    FlexLove.init({
      baseScale = { width = 1920, height = 1080 },
      immediateMode = true
    })

### Batch Element Creation

    -- ❌ Bad: Creating elements in love.draw() (retained mode)
    function love.draw()
      local button = FlexLove.new({ ... })  -- DON'T DO THIS
    end

    -- ✅ Good: Create once, reuse
    local button
    function love.load()
      button = FlexLove.new({ ... })
    end

    function love.draw()
      FlexLove.draw()  -- Just draw existing elements
    end

### Limit Layout Recalculations

    -- Avoid changing layout properties every frame
    local element = FlexLove.new({ ... })

    -- ❌ Bad: Causes relayout every frame
    function love.update(dt)
      element.width = math.sin(love.timer.getTime()) * 100
    end

    -- ✅ Good: Update only when needed
    function love.update(dt)
      if someCondition then
        element.width = newWidth
      end
    end

### Use Element Pooling for Dynamic Content

    -- Create a pool of reusable elements
    local itemPool = {}
    for i = 1, 50 do
      itemPool[i] = FlexLove.new({
        parent = scrollContainer,
        visible = false,
        text = "",
        width = "100%",
        themeComponent = "cardv2"
      })
    end

    -- Reuse elements instead of creating new ones
    function updateList(items)
      for i, item in ipairs(items) do
        if itemPool[i] then
          itemPool[i].text = item.name
          itemPool[i].visible = true
        end
      end

      -- Hide unused elements
      for i = #items + 1, #itemPool do
        itemPool[i].visible = false
      end
    end

💡 Tip: Use retained mode when you want the lightest footprint possible, immediate mode is much faster to prototype with and relative low overhead.

View Complete Example →

### 🔗 More Examples

Find all complete working examples in the examples/ directory:

- `advanced_layout.lua` - Complex nested layouts with flex and grid
- `theme_custom_components.lua` - Theme switching and custom components
- `stateful_ui.lua` - Interactive state management patterns
- `scrollable_content.lua` - Scrolling with backdrop blur
- `slider_example.lua` - Slider controls for settings menus
- `input_handling.lua` - Comprehensive input event handling
- `performance_example.lua` - Performance optimization techniques