Ribbon UI System

January 17, 2026 · View on GitHub

Overview

The ribbon UI replaces the traditional menu bar with a modern, icon-based interface that organizes controls into logical groups. The ribbon provides quick access to common actions while preserving all keyboard shortcuts.

Architecture

Module Structure

src/ui/
├── mod.rs        # UI module exports (Ribbon, RibbonAction)
└── ribbon.rs     # Ribbon implementation

Key Types

RibbonAction Enum

Defines all actions that can be triggered from the ribbon:

pub enum RibbonAction {
    // File operations
    New,              // Create new file/tab (Ctrl+N)
    Open,             // Open file dialog (Ctrl+O)
    OpenWorkspace,    // Open folder/workspace dialog
    CloseWorkspace,   // Close current workspace
    Save,             // Save current file (Ctrl+S)
    SaveAs,           // Save As dialog (Ctrl+Shift+S)

    // Workspace operations (only visible in workspace mode)
    SearchInFiles,    // Search in files (Ctrl+Shift+F)
    QuickFileSwitcher, // Quick file switcher (Ctrl+P)

    // Edit operations
    Undo,             // Undo last change (Ctrl+Z)
    Redo,             // Redo last undone change (Ctrl+Y)

    // Formatting operations (Markdown)
    Format(MarkdownFormatCommand), // Apply markdown formatting

    // Structured data operations (JSON/YAML/TOML)
    FormatDocument,   // Format/pretty-print structured data
    ValidateSyntax,   // Validate syntax

    // View operations
    ToggleViewMode,   // Toggle Raw/Rendered (Ctrl+E)
    ToggleLineNumbers, // Toggle line numbers visibility
    ToggleSyncScroll, // Toggle sync scrolling

    // Tools
    FindReplace,      // Find/Replace dialog (Ctrl+F/Ctrl+H)
    ToggleOutline,    // Toggle outline panel

    // Export operations
    ExportHtml,       // Export as HTML file
    CopyAsHtml,       // Copy rendered HTML to clipboard

    // Settings
    CycleTheme,       // Cycle through themes (Ctrl+Shift+T)
    OpenSettings,     // Open settings panel (Ctrl+,)

    // Ribbon control
    ToggleCollapse,   // Collapse/expand ribbon
}

Ribbon Struct

Manages ribbon state and rendering:

pub struct Ribbon {
    collapsed: bool,  // Whether ribbon is in collapsed mode
}

Layout

The ribbon appears below the title bar and above the tab bar:

┌────────────────────────────────────────────────────────────────┐
│ 📝 Document.md - Ferrite                       [_][□][×]      │  Title Bar
├────────────────────────────────────────────────────────────────┤
│ ◀ | File  📄 📂 💾 📥 | Edit  ↩ ↪ | View  👁 🔢 | Tools 🔍 | ⚙ │  Ribbon
├────────────────────────────────────────────────────────────────┤
│ [Tab 1] [Tab 2*] [+]                                          │  Tab Bar
├────────────────────────────────────────────────────────────────┤
│                                                                │
│                        Editor Content                          │
│                                                                │
├────────────────────────────────────────────────────────────────┤
│ Untitled | Ln 1, Col 1 | UTF-8 | 0 words                      │  Status Bar
└────────────────────────────────────────────────────────────────┘

Button Groups

File Group

IconActionShortcutDescription
📄NewCtrl+NCreate new file/tab
📂OpenCtrl+OOpen file dialog
💾SaveCtrl+SSave current file
📥Save AsCtrl+Shift+SSave with new name

Edit Group

IconActionShortcutDescription
UndoCtrl+ZUndo last change
RedoCtrl+YRedo undone change

View Group

IconActionShortcutDescription
📝/👁Toggle ViewCtrl+Shift+VSwitch Raw/Rendered
🔢/#Line Numbers-Toggle line numbers

Tools Group

IconActionShortcutDescription
🔍Find/Replace-Find/Replace (placeholder)

Settings Group (right-aligned)

IconActionShortcutDescription
🎨ThemeCtrl+Shift+TCycle theme
Settings-Open settings (placeholder)

Collapsible Behavior

The ribbon supports two states:

  1. Expanded (default): Shows group labels and full-height buttons (40px)
  2. Collapsed: Icon-only mode with reduced height (28px)

Toggle with the ◀/▶ button on the left side of the ribbon.

Theme Integration

The ribbon adapts to the current theme:

// Ribbon background colors
let ribbon_bg = if is_dark {
    Color32::from_rgb(40, 40, 40)   // Dark theme
} else {
    Color32::from_rgb(248, 248, 248) // Light theme
};

// Separator colors
let separator_color = if is_dark {
    Color32::from_rgb(70, 70, 70)
} else {
    Color32::from_rgb(210, 210, 210)
};

Usage in App

// In FerriteApp struct
ribbon: Ribbon,

// In render_ui method
let ribbon_action = {
    let mut action = None;
    egui::TopBottomPanel::top("ribbon")
        .frame(/* frame config */)
        .show(ctx, |ui| {
            action = self.ribbon.show(
                ui,
                &theme_colors,
                view_mode,
                show_line_numbers,
                can_undo,
                can_redo,
                can_save,
            );
        });
    action
};

// Handle ribbon actions
if let Some(action) = ribbon_action {
    self.handle_ribbon_action(action, ctx);
}

State Parameters

The show() method requires current application state:

ParameterTypePurpose
theme_colors&ThemeColorsTheme-aware styling
view_modeViewModeCurrent view mode (Raw/Rendered)
show_line_numbersboolLine numbers visibility
can_undoboolEnable/disable Undo button
can_redoboolEnable/disable Redo button
can_saveboolEnable/disable Save button

Button Styling

Icon buttons use consistent styling:

const ICON_BUTTON_SIZE: Vec2 = Vec2::new(32.0, 28.0);

fn icon_button(ui, icon, tooltip, enabled, is_dark) -> Response {
    // Disabled state uses muted colors
    let text_color = if enabled {
        theme_text_color
    } else {
        muted_color
    };
    
    // Hover effect
    if btn.hovered() && enabled {
        ui.painter().rect_filled(btn.rect, 3.0, hover_bg);
    }
}

Testing

#[test]
fn test_ribbon_new() {
    let ribbon = Ribbon::new();
    assert!(!ribbon.is_collapsed());
}

#[test]
fn test_ribbon_toggle_collapsed() {
    let mut ribbon = Ribbon::new();
    ribbon.toggle_collapsed();
    assert!(ribbon.is_collapsed());
}

#[test]
fn test_ribbon_height() {
    let mut ribbon = Ribbon::new();
    assert_eq!(ribbon.height(), 40.0); // expanded
    ribbon.toggle_collapsed();
    assert_eq!(ribbon.height(), 28.0); // collapsed
}

Keyboard Shortcuts

All existing keyboard shortcuts continue to work independently of the ribbon:

ShortcutAction
Ctrl+NNew file
Ctrl+OOpen file
Ctrl+SSave
Ctrl+Shift+SSave As
Ctrl+ZUndo
Ctrl+YRedo
Ctrl+Shift+ZRedo (alternative)
Ctrl+Shift+VToggle view mode
Ctrl+Shift+TCycle theme
Ctrl+TNew tab
Ctrl+WClose tab
Ctrl+TabNext tab
Ctrl+Shift+TabPrevious tab

Implemented Actions

All ribbon buttons are now fully functional:

  • Find/Replace (🔍): Opens find/replace dialog (Ctrl+F / Ctrl+H)
  • Settings (⚙): Opens settings panel modal (Ctrl+,)
  • Export HTML: Exports document as styled HTML file
  • Copy as HTML: Copies rendered HTML to clipboard
  • Outline (📋): Toggles document outline panel