← Andrei Kabylin

Lab

Short demos of side projects and experiments

Comic reader

Wide spread pages in the vertical reading mode: the reader shows them at full height and lets you scroll them sideways without leaving the vertical feed. This is the reader I built at Comikey; the clip shows After God, which you can read there.

Custom Flutter widgets

A chart and a date picker for a currency trends app, built from scratch because no existing package could match the design's exact look and feel. Both are open source.

A trend chart drawn with plain math and Canvas, no chart packages, with animated transitions between time ranges and currency pairs. Source.

A date picker in the spirit of the Cupertino wheel, with its own scroll math and haptic feedback on every tick. Wheel source, date range sheet.

Cross-platform docking

A docking system where all the layout math lives in a shared Rust core and the UI is fully native: SwiftUI on Apple platforms and Jetpack Compose on Android. The native UIs talk to the core through Tech Paws Buffers, a binary protocol I designed in place of hand-written FFI and JNI bindings. It's inspired by gRPC, but built for in-process calls fast enough for 120 fps interaction rather than networking: a schema language, a code generator and a runtime that carries calls between Rust and Swift, Kotlin or Dart. The code generator is open source; the runtime isn't yet. These clips show the macOS build.

Show the docking schema

The interface between the Rust core and the native UIs, written in the Tech Paws Buffers schema language. The generator turns it into Rust, Swift and Kotlin code. Functions are calls into the core; signals are observable values the core publishes, which SwiftUI and Compose views subscribe to and re-render from.

#[rust(use = "tech_paws_graphics::EdgeInsets")]
#[rust(use = "crate::docking::*")]

#[kotlin(package = "com.tech_paws.buffers_generated.ui")]
#[kotlin(import = "com.tech_paws.buffers_generated.EdgeInsets")]

struct DockingCalculatedArea {
    id: u64,
    x: f64,
    y: f64,
    width: f64,
    height: DockingWindowCalculatedHeight,
    side: DockingSide,
    is_max_sized: bool,
    is_float: bool,
    has_shrunk: bool,
    snap_x_enabled: bool,
    snap_y_enabled: bool,
    show_left_resize: bool,
    show_right_resize: bool,
    show_top_resize: bool,
    show_bottom_resize: bool,
    is_visible: bool,
}

struct DockingCalculatedContentArea {
    x: f64,
    y: f64,
    width: f64,
    height: f64,
}

struct DockingWindowConfig {
    id: u64,
    min_width: Option<f64>,
    min_height: Option<f64>,
    max_width: Option<f64>,
    max_height: Option<f64>,
    width: f64,
    height: DockingWindowHeight,
    resize: DockingResize,
    side: DockingSide,
    is_max_sized: bool,
    is_float: bool,
    is_master: bool,
    auto_resize: bool,
    size_snap_x: Option<f64>,
    size_snap_y: Option<f64>,
    size_padding_left: f64,
    size_padding_right: f64,
    size_padding_top: f64,
    size_padding_bottom: f64,
    is_visible: bool,
    resize_vertically_on_drag: bool,
}

enum DockingResize {
    None,
    Horizontal,
    Vertical,
    Side,
    Free,
}

enum DockingSide {
    Left,
    Top,
    Right,
    Bottom,
}

enum DockingWindowHeight {
    Value {
        size: f64,
    },
    Wrap,
}

enum DockingWindowCalculatedHeight {
    Value {
        size: f64,
    },
    Wrap {
        size: f64,
    },
}

struct DockingPlaceholder {
    placeholder_id: u64,
    window_id: u64,
    x: f64,
    y: f64,
    width: f64,
    height: f64,
    min_width: Option<f64>,
    min_height: Option<f64>,
    is_max_sized: bool,
    edge: PlaceholderEdge,
    side: DockingSide,
}

enum PlaceholderEdge {
    Left,
    Right,
    Top,
    Bottom,
}

enum DockingDragMode {
    None,
    Move,
    LeftResize,
    RightResize,
    TopResize,
    BottomResize,
}

enum DockingSides {
    Vertical,
    Horizontal,
    Free,
}

struct DockingViewPaddings {
    paddings: EdgeInsets,
    animated: bool,
}

trait DockingRpc {
    signal placeholder -> Option<DockingPlaceholder>;

    signal swap_window_id -> Option<u64>;

    signal update_calculated_areas -> Vec<DockingCalculatedArea>;

    fn init_config(
        sides: DockingSides,
        config: Vec<DockingWindowConfig>
    ) -> Vec<DockingWindowConfig>;

    fn set_parent_view_area_size(width: f64, height: f64);

    fn set_window_wrap_height(id: u64, height: f64);

    fn calculate_areas() -> Vec<DockingCalculatedArea>;

    fn get_content_area() -> DockingCalculatedContentArea;

    fn set_config(config: Vec<DockingWindowConfig>);

    fn is_window_wrap_sized(id: u64) -> bool;

    fn on_drag_resize_start(
        id: u64,
        x: f64,
        y: f64,
        dragMode: DockingDragMode,
    );

    fn on_drag_move_start(
        id: u64,
        x: f64,
        y: f64,
        relative_x: f64,
        relative_y: f64,
    );

    fn on_drag_start(
        id: u64,
        x: f64,
        y: f64,
        relative_x: f64,
        relative_y: f64,
    ) -> DockingDragMode;

    fn on_drag_update(x: f64, y: f64);

    fn on_drag_end();

    fn set_visibility(id: u64, is_visible: bool);
}

Panels can be dragged, docked and stacked freely.

Panels resize by dragging their edges, with optional grid snapping.

Any panel can be hidden and shown again, and the layout stays intact.

Splits work too: here a full-height dock sits next to the main content area.