You have to implement plans in ./plans/ui-upgrade. Some plans are already complete check ui_upgrade_plan.md for status. You must implement every plan and make a commit mention which plan was completed.
NOTE: Plan 1 & 2 are already complete before starting this loop.
Phases 1-7 were already complete. Phase 8 is now DONE:
font_features: Vec<(String, i32)>field on Element and TextSpanfont_variations: Vec<(String, f32)>field on Element and TextSpan.font_feature("tnum", 1)builder method for OpenType features (tabular numbers, ligature control).font_variation("wght", 700.0)builder method for variable font axes (weight, width, etc.)- Applied via
TextStyle::add_font_feature()in renderer for both Text and RichText - Variable fonts via
TextStyle::set_font_arguments()withVariationPositioncoordinates - Per-span font features and variations in RichText for mixed typography
- Threaded through DrawCommand::Text and DrawCommand::RichText
Phase 9 (BiDi/i18n) is DONE:
TextDirectionenum (Ltr, Rtl) instyle.rstext_directionandlocalefields on Element with.text_direction(),.rtl(),.locale()builder methods- Same fields on
TextSpanfor per-span BiDi/locale in RichText - Threaded through
DrawCommand::TextandDrawCommand::RichText ParagraphStyle::set_text_direction()for RTL/LTR paragraph directionTextStyle::set_locale()for proper CJK/Arabic line breaking rules- Applied in both
draw_text()anddraw_rich_text()renderer functions
Phase 10 (Inline Placeholders) is DONE:
PlaceholderAlignmentenum (Baseline, AboveBaseline, BelowBaseline, Top, Bottom, Middle)InlinePlaceholderstruct with width, height, alignment, optional ImageSourceRichSpanenum replacingTextSpanin RichText:Text(TextSpan)|Placeholder(InlinePlaceholder)ParagraphBuilder::add_placeholder()with SkiaPlaceholderStyleparagraph.get_rects_for_placeholders()to position inline elements after layout- Images rendered at placeholder positions via
draw_image() .inline_placeholder()and.inline_image()builder methods on Element
Phase 11 (Performance Optimization) is DONE:
- Paragraph cache:
HashMap<u64, ParagraphCacheEntry>keyed by content+style hash (excludes position/width/cursor state) - Cache hit with same width: reuse paragraph directly (zero rebuild cost)
- Cache hit with different width: re-layout only (skip expensive shaping/building)
- Cache miss: build, layout, cache for next frame
- Frame-based LRU eviction: entries unused for 60+ frames swept every 120 frames
- Hard cap at 512 entries with full clear on overflow
begin_frame()method on SkiaRenderer for frame counter and eviction- FontCollection pre-warming: resolves
sans-serif,serif,monospace,system-uiat startup - Text input overlay extracted to
draw_text_input_overlay_static()for cache-compatible rendering Hash+Eqderived onColor,TextAlign,TextDirection,TextDecorationStylefor cache key computation
Plan 1 is now COMPLETE. All 11 phases implemented.
Phases 1-6, 8, 10, 11, and 12 of the event system plan (plans/ui-upgrades/3-events.md) are DONE.
Phase 7 Task 6 (Render preedit text) is DONE:
- Added
preedit_textandpreedit_cursorfields to Element withpreedit()builder method - Added
preedit_byte_rangefield to DrawCommand::Text - Preedit text is composed inline at the cursor position in TextInput display text
- Cursor is positioned within the preedit text using
preedit_cursor - Renderer draws underline decoration beneath preedit text range
- Selection is cleared during composition (standard IME behavior)
The remaining deferred tasks are:
- Phase 9 Tasks 6-8: Drag ghost rendering + external DnD via wl_data_device (needs VM)
- Phase 10 Tasks 5-7: Wayland clipboard integration via wl_data_device (needs VM)
All event types, handler slots, dispatch logic, and Wayland protocol handlers are implemented.
Phase 1 (core visual upgrades) is DONE:
- Per-corner border radius (
CornerRadiitype replacingcorner_radius: f32) - Gradient backgrounds (linear, radial, conic via
Gradientenum +DrawCommand::GradientRect) - Inset box shadows (
DrawCommand::InsetBoxShadowwith Chromium-style EvenOdd path clipping) - Multiple box shadows (
Vec<BoxShadow>on Element, both outset and inset)
Phase 2 (borders and outlines) is DONE:
- BorderStyle enum (Solid, Dashed, Dotted, Double, None)
- Per-side borders via FullBorder (BorderSide per side with clip-based drrect rendering)
- Outline support (offset stroke outside element, does not affect layout)
- Border style rendering: dashed via PathEffect::dash, dotted via round caps, double via two strokes
Phase 3 (CSS filters) is DONE:
- Filter enum with all CSS filter functions (blur, brightness, contrast, grayscale, sepia, hue-rotate, invert, opacity, saturate, drop-shadow)
- PushFilter/PopFilter DrawCommands wrapping element content with chained ImageFilters
- ApplyBackdropFilter for backdrop-filter support (blur and all color matrix filters)
- Builder methods: filter_blur(), filter_brightness(), filter_contrast(), etc.
- Chained filter composition via input parameter (each filter feeds into next)
Phase 4 (transforms) is DONE:
- Transform enum (Translate, Rotate, Scale, Skew, Matrix)
- transform_origin as fraction of element bounds (default center 0.5, 0.5)
- PushTransform/PopTransform DrawCommands with origin-based application
- Builder methods: rotate(), scale(), scale_xy(), translate(), skew(), transform_origin()
- Combined transforms applied in CSS order via sequential canvas operations
Phase 5 (text enhancements) was already fully implemented in prior work.
Phase 6 (remaining properties) is DONE:
- visibility:
visiblefield on Element,hidden()builder — invisible elements skip drawing but children still render - BlendMode enum (16 CSS blend modes mapped to Skia) with PushBlendMode/PopBlendMode
- Color utilities:
from_hex(),from_hsl(),from_hsla(),with_alpha(),lighter(),darker()
Phase 7 (Polish and Optimization) is DONE:
- Multiple backgrounds:
Backgroundenum (Solid/Gradient/Image) withVec<Background>on Element,add_background()builder - Background clip/origin:
BackgroundClipenum (BorderBox/PaddingBox/ContentBox),BackgroundOriginenum, builders - Border image:
BorderImagestruct with nine-slice rendering viadraw_image_nine(),border_image()andborder_image_width()builders - Gradient shader cache:
HashMap<u64, Shader>keyed by gradient+bounds hash, 256-entry cap, avoids redundant shader creation - Image loading helper:
load_image()method extracted for reuse across border-image and background-image BackgroundLayersandBorderImageDrawCommand variants with full renderer implementations
Plan 4 is now COMPLETE. All 7 phases implemented.
Phase 1 (Tier 1 improvements) is DONE:
- Fixed cache key generation: content hash + dimensions instead of truncated string
- LRU cache eviction with 64MB byte budget
- Color tinting via
tint(color)builder using SrcIn blend color filter - ImageFit enum (Contain, Cover, Fill, ScaleDown) with
image_fit()builder - Proper image fit computation for all modes
Phase 2 (SVG path parser) is DONE:
- SVG path
dattribute parser supporting M/m, L/l, H/h, V/v, C/c, Q/qx, Z/z commands - ElementKind::Shape with ShapeData (path_data, fill, stroke, viewbox)
- DrawCommand::Path variant with viewbox-to-bounds scaling
- shape() and shape_with_viewbox() builder functions
- Path rendering with fill and stroke in SkiaRenderer
Phase 3 (Tier 2 Vector SVG Renderer) is DONE:
svg_render.rsmodule: usvg tree walker emitting native Skia draw callsVectorSvgstruct: parses SVG → usvg tree → records Skia Picture for resolution-independent replay- Full usvg node support: Group (transform, opacity, clip-path), Path (fill, stroke, dash, cap, join), Image (PNG/JPEG/GIF/WEBP/nested SVG), Text (via flattened paths)
- SVG paint conversion: solid colors, linear gradients, radial gradients (pattern fallback to gray)
- Clip path support with per-path fill rules
ImageSource::VectorSvgvariant with content hash cache in SkiaRenderervector_svg()andvector_svg_file()builder functions- Image fit support (Contain, Cover, Fill, ScaleDown) for vector SVGs
- Tint support via SrcIn blend on layer
Phase 4 (Icon System) is DONE:
icon_registry.rsmodule withIconRegistry,IconPack,ResolvedIcontypes- Multi-tier icon resolution: inline SVG → icon packs → freedesktop themes → pixmaps fallback
IconRegistry::register()for inline SVG iconsIconRegistry::register_pack()andregister_pack_from_dir()for icon pack loading- Freedesktop theme search across multiple sizes, categories, and themes (breeze-dark, hicolor)
ElementKind::Iconwithicon()builder function (default 24x24)DrawCommand::Iconresolved at render time via IconRegistry- Auto-selects vector SVG for .svg files, rasterized for .png
- Tint and image fit support on icons
- Gray placeholder for unresolved icons
Phase 5 (SVG Filters) is DONE:
- Full filter pipeline:
build_filters()composes usvg filter chains into SkiaImageFilters - Filter primitives mapped: GaussianBlur, Offset, ColorMatrix (matrix/saturate/hueRotate/luminanceToAlpha), ComponentTransfer, Composite (over/in/out/atop/xor/arithmetic), Blend (all 16 SVG modes), Morphology (erode/dilate), DropShadow, Flood, Merge, Tile
- Named result tracking for cross-referenced filter inputs (
result/inattributes) - SourceGraphic and SourceAlpha input resolution
- Color matrix helpers:
saturate_matrix(),hue_rotate_matrix(),luminance_to_alpha_matrix() - Transfer function table builder for all 5 types (identity, table, discrete, linear, gamma)
- Applied via
SaveLayerRecwithImageFilterpaint inrender_group() - Skipped: lighting filters (feDiffuseLighting/feSpecularLighting), feTurbulence, feImage, feConvolveMatrix, feDisplacementMap (uncommon in UI icons)
Phase 6 (Advanced SVG Features — partial) is DONE:
- Masks:
apply_mask()renders mask content withDstInblend mode- Luminance masks convert RGB to alpha via color matrix filter
- Alpha masks use mask content alpha directly
- Nested/chained masks supported via recursion
- Patterns:
usvg_paint_to_skia()now renders pattern content into SkiaPicturePicture::to_shader()withTileMode::Repeatfor tiling- Pattern transform applied via local matrix
- Replaces previous gray fallback
- Text rendering from usvg was already done (flattened paths in Phase 3)
- Remaining: multi-resolution caching/HiDPI, async loading (lower priority)
Phase 7 (SVG DOM — Tier 3) is DONE:
- SvgDocument: Lightweight document model wrapping usvg tree with flat element list and ID map
- SvgElement: Per-element overrides for fill, stroke, opacity, visibility, transform
- SvgElementKind: Group, Path, Text, Image variants
- SvgPaint/SvgStroke: Simplified paint types for element-level overrides
- from_data(): Parses SVG → usvg tree → walks and builds element list with ID indexing
- Query API:
element_by_id(),element_by_id_mut(),element_ids(),element_count() - Modification API:
set_fill(),set_stroke(),set_opacity(),set_visible(),set_transform(),set_text() - Dirty flag: Modifications mark document dirty;
update()re-records Skia Picture only when needed - Override rendering:
render_with_overrides()applies element-level overrides during re-recording - Hit testing:
hit_test()andhit_test_in_bounds()for bounds-based element detection - ElementKind::SvgDocument: Arc<Mutex> for shared ownership with
svg_document()builder - DrawCommand::SvgDocument: Renders via
draw_fit()with tint and image fit support - SVG event callbacks:
on_svg_click()andon_svg_hover()builder methods for SVG sub-element events - Exported:
SvgDocument,SvgElement,SvgElementKind,SvgPaint,SvgStrokefrom lib.rs
Plan 5 is now COMPLETE. All 7 phases implemented.
Phase 1 (focus management additions) is DONE:
disabled,read_only,error,labelfields on Element with builder methodsindeterminate,loading,show_value,progress_color,track_colorform propsFocusRingDrawCommand variant for rendering 2px blue outline around focused elementsLineandCircleDrawCommand variants for form element rendering
Phase 2 (enhanced text input) is DONE:
TextInputVariantenum (Text, Password, Email, Url, Search, Number, Tel)- Enhanced
TextInputStatewith: selection anchor, undo/redo stacks, word boundary movement - Selection methods:
select_all(),select_left/right(),select_word_left/right(),select_to_start/end(),select_word_at() - Word-level operations:
move_word_left/right(),delete_word_back(),delete_word_forward() - Undo/redo:
undo(),redo()with grouped edit entries - IME composition state:
composing,compose_text,compose_cursor - Password reveal timer for mobile-style character flash
password_input()builder function
Phase 3 (Button, Checkbox, Radio, Switch) is DONE:
ElementKind::ButtonwithButtonVariant(Primary, Secondary, Ghost, Danger)ElementKind::Checkboxwith checked/indeterminate state and labelElementKind::Radiowith selected, group, value, labelElementKind::Switchwith on/off state and label- Display list rendering: background colors, checkmark path, radio circle, switch track+thumb
- Builder functions:
button(),checkbox(),radio(),switch()withlabel(),variant(),indeterminate()
Phase 4 (Slider) is DONE:
ElementKind::Sliderwith value, min, max, stepElementKind::RangeSliderwith low, high, min, max, step- Track rendering: filled/empty portions with rounded ends
- Thumb rendering: white circle with shadow
- Builder functions:
slider(),range_slider(),step(),show_value(),progress_color(),track_color()
Phase 5 (Progress Bar and Spinner) is DONE:
ElementKind::ProgresswithProgressVariant(Bar, Circular)- Determinate bar: track + fill with rounded ends
- Indeterminate bar: 30% width segment (animation handled by runtime)
- Circular spinner: track circle + arc stroke
- Builder functions:
progress(),progress_indeterminate(),spinner()
Phase 7 (Select/Dropdown) is DONE:
ElementKind::Selectwith options, selected index, placeholderSelectStatefor keyboard navigation and type-ahead search- Inline dropdown rendering with scroll, highlight, checkmark
- Builder function:
select()
Phase 8 (Textarea) is DONE:
ElementKind::Textareawith multiline textTextareaStatewith line wrapping, line numbers, tab handling- Vertical/horizontal scrolling, selection across lines, undo/redo
- Builder function:
textarea()
Theming system is DONE:
Themestruct with 40+ color/sizing tokens inui/src/theme.rsTheme::dark()andTheme::light()presets- Theme flows through
build_display_list()→emit_commands()→emit_select() - All form element rendering uses theme colors instead of hardcoded values
- Views can override via
View::theme()trait method - Per-element overrides still take precedence over theme
Phase 10 (Color Picker — pre-built) is DONE (partial):
Hsvstruct withto_color(),to_color_with_alpha(),hue_color()methodsColor::to_hsv(),Color::from_hsv(),Color::from_hsva()conversionsColor::to_hex(),Color::to_hsl(),Color::lerp()utilitiesColor::luminance(),Color::is_dark(),Color::contrast_text()for accessibilityElementKind::ColorPickerwithcolor_picker()builder- Closed-state rendering: color swatch + hex label + chevron
ColorPickerStatewith full HSV state machine (open/close, SV drag, hue drag, alpha drag)ColorPickerDragenum for tracking active drag targetparse_hex_color()supporting #RGB, #RGBA, #RRGGBB, #RRGGBBAA- Hex input with live validation, commit/revert, sync-while-unfocused
- Position query methods:
sv_position(),hue_position(),alpha_position() - 26 unit tests covering state, drags, hex parsing, color roundtrips
- Remaining: popup surface with SV gradient, hue slider, alpha slider (needs VM)
Phase 11 (Date/Time Picker — pre-built) is DONE (partial):
DatePickerVariantenum (Date, Time, DateTime)ElementKind::DatePickerwithdate_picker(),time_picker(),datetime_picker()builders- Closed-state rendering: formatted value or placeholder + calendar/clock icon
DatePickerStatewith full calendar navigation state machineCalendarDatewith ISO parse/format, weekday calculation, orderingTimeValuewith parse/format, hour/minute increment/decrement with wrapping- Calendar grid generation (
calendar_grid()→ weeks of 7 day-slots) - Month/year navigation, go-to-today, min/max date range validation
DatePickerFocusenum for sub-control focus tracking- 28 unit tests covering date parsing, calendar math, navigation, grid generation
- Remaining: popup calendar/time selector UI rendering (needs VM)
Phase 12 (File Input — pre-built) is DONE (partial):
ElementKind::FileInputwithfile_input()builder,acceptfilter,multipleflag- Closed-state rendering: "Choose File" button + file count label
- Remaining: native file dialog integration via DBus/portal (needs VM)
Phase 6 (Popup Infrastructure — pre-built) is DONE (partial):
PopupConfigstruct with anchor, size, margin, keyboard interactivity, on_focus_lost callback- Builder API:
PopupConfig::new(),.margin(),.keyboard(),.on_focus_lost() PopupRequestenum (Open/Close) for request batchingRenderContextpopup methods:open_popup(),close_popup(),is_popup_open(),render_popup()- Popup request/element collection:
set_open_popups(),take_popup_requests(),take_popup_elements() - Remaining:
PopupStatein wayland.rs, LayerSurface creation, multi-surface dispatch (needs VM)
Remaining phases needing VM: Phase 6 (popup surface creation), Phase 9 (IME protocol binding), popup rendering of Phases 10-12.
Plan 6 Phases 1-8 + Theming + Phase 6 types + Phases 10-12 (pre-built) are DONE.
- Phase 1: Three-phase event propagation (capture/target/bubble) — DONE
- Phase 2: Focus management and tab navigation — DONE
- Phase 3: Pointer capture — DONE
- Phase 4: Click synthesis (double-click, context menu) — DONE
- Phase 5: Scroll events (WheelEvent, ScrollEnd, source discrimination) — DONE
- Phase 6: Keyboard events (KeyDown/KeyUp, key repeat, modifier tracking) — DONE
- Phase 7: Text Input and IME (Tasks 1-5 done, Task 6 deferred) — DONE
- Phase 8: Drag support (legacy) — DONE
- Phase 9: Drag and Drop (Tasks 1-5 done, Tasks 6-9 deferred) — DONE
- Phase 10: Clipboard (event types, handlers, Ctrl+C/X/V dispatch, default paste for text inputs) — DONE
- Phase 11: Touch Events (TouchEvent type, touch-to-pointer coercion, native dispatch, wl_touch handler) — DONE
- Phase 12: Hit Testing Enhancements — DONE