Clean up build warnings

Resolve all compiler warnings across daw-backend, lightningbeam-core, and
lightningbeam-editor:

- Delete dead code: the superseded CPU raster tools in raster_tool.rs
  (EffectBrush/Smudge/Gradient/Transform/Warp/Liquify/Selection — replaced by
  the GPU path), plus orphaned helpers and never-read struct fields.
- Mechanical fixes: drop unused imports/variables/mut, underscore unused params,
  `drop(&x)` -> `let _ = x`, deprecated egui::Rounding -> CornerRadius, snake_case
  rename, elided-lifetime Cow<'_, [u8]>.
- Keep the WIP CSS theming system (theme.rs/theme_render.rs) under
  #[allow(dead_code)] rather than deleting it.

Editor checks warning-free; 293 core tests pass.
This commit is contained in:
Skyler Lehmkuhl 2026-06-21 16:42:50 -04:00
parent 3c3a482e2e
commit 83057b754a
18 changed files with 30 additions and 715 deletions

View File

@ -116,7 +116,6 @@ pub struct Engine {
// Current tempo map — kept in sync with SetTempo/SetTempoMap commands.
tempo_map: crate::TempoMap,
current_fps: f64,
// Current time signature — updated by SetTempo, used when SetTempoMap fires.
time_sig: (u32, u32),
}
@ -197,7 +196,6 @@ impl Engine {
timing_sum_total_us: 0,
timing_overrun_count: 0,
tempo_map: crate::TempoMap::constant(120.0),
current_fps: 30.0,
time_sig: (4, 4),
}
}

View File

@ -12,7 +12,7 @@
//! correct). If the base is somehow not resident we skip rather than corrupt.
/// Normalize a buffer to full length `n`; an empty/short buffer becomes transparent.
fn normalize(buf: &[u8], n: usize) -> std::borrow::Cow<[u8]> {
fn normalize(buf: &[u8], n: usize) -> std::borrow::Cow<'_, [u8]> {
if buf.len() == n {
std::borrow::Cow::Borrowed(buf)
} else {

View File

@ -1161,7 +1161,7 @@ impl VectorGraph {
/// faces that lie inside it (inheriting its colour/rule); faces outside it — or in a
/// hole — are dropped.
fn retrace_fills_after_cut(&mut self, new_edges: &[EdgeId]) {
use kurbo::{ParamCurve, Shape};
use kurbo::Shape;
let new_set: HashSet<EdgeId> = new_edges
.iter()
.filter(|&&e| !e.is_none() && !self.edges[e.idx()].deleted)
@ -1642,21 +1642,6 @@ impl VectorGraph {
// Boundary tracing internals
// -------------------------------------------------------------------
/// Find the nearest non-deleted edge to a point. Returns (EdgeId, t, distance).
fn nearest_edge_to_point(&self, point: Point) -> Option<(EdgeId, f64, f64)> {
let mut best: Option<(EdgeId, f64, f64)> = None;
for (i, e) in self.edges.iter().enumerate() {
if e.deleted {
continue;
}
let eid = EdgeId(i as u32);
let (t, dist) = nearest_point_on_cubic(&e.curve, point);
if best.is_none() || dist < best.unwrap().2 {
best = Some((eid, t, dist));
}
}
best
}
/// Build a BezPath from a boundary (without storing it as a fill).
/// Handles `EdgeId::NONE` separators to start new contours (holes).

View File

@ -43,8 +43,6 @@ pub enum CurveEditAction {
#[derive(Clone, Debug)]
pub struct CurveDragState {
pub keyframe_index: usize,
pub original_time: f64,
pub original_value: f32,
pub current_time: f64,
pub current_value: f32,
}
@ -263,8 +261,6 @@ pub fn render_curve_lane(
let kf = &keyframes[idx];
*drag_state = Some(CurveDragState {
keyframe_index: idx,
original_time: kf.time,
original_value: kf.value,
current_time: kf.time,
current_value: kf.value,
});

View File

@ -17,8 +17,6 @@ pub struct DocumentHint {
pub has_raster: bool,
pub has_vector: bool,
pub current_time: f64,
pub doc_width: u32,
pub doc_height: u32,
}
/// Export type selection
@ -577,7 +575,7 @@ impl ExportDialog {
if ui.button("Choose location...").clicked() {
let ext = self.current_extension();
let mut dialog = rfd::FileDialog::new()
let dialog = rfd::FileDialog::new()
.set_directory(&self.output_dir)
.set_file_name(&self.output_filename)
.add_filter(ext.to_uppercase(), &[ext]);

View File

@ -567,7 +567,7 @@ impl ExportOrchestrator {
let gpu = state.gpu_resources.as_mut().unwrap();
let output_view = state.output_texture_view.as_ref().unwrap();
let mut encoder = video_exporter::render_frame_to_gpu_rgba(
let encoder = video_exporter::render_frame_to_gpu_rgba(
document,
state.settings.time,
w, h,

View File

@ -1586,11 +1586,6 @@ impl GpuBrushEngine {
crate::debug_overlay::update_gpu_memory(count, total);
}
/// Get the cached display texture for a raster layer keyframe.
pub fn get_layer_texture(&self, kf_id: &Uuid) -> Option<&CanvasPair> {
self.raster_layer_cache.get(kf_id)
}
/// Ensure a low-res proxy texture exists for `kf_id` (uploaded once; proxies are
/// immutable). Bumps recency and evicts the least-recently-used past the budget.
/// `pixels` is sRGB-premultiplied RGBA of length `w * h * 4`.
@ -1778,18 +1773,6 @@ impl GpuBrushEngine {
self.displacement_bufs.insert(id, DisplacementBuffer { buf, width, height });
}
/// Overwrite the displacement buffer contents with the provided data.
pub fn upload_displacement_buf(
&self,
queue: &wgpu::Queue,
id: &Uuid,
data: &[[f32; 2]],
) {
if let Some(db) = self.displacement_bufs.get(id) {
queue.write_buffer(&db.buf, 0, bytemuck::cast_slice(data));
}
}
/// Zero out a displacement buffer (reset all displacements to (0,0)).
pub fn clear_displacement_buf(&self, queue: &wgpu::Queue, id: &Uuid) {
if let Some(db) = self.displacement_bufs.get(id) {

View File

@ -2234,40 +2234,6 @@ impl EditorApp {
}
}
/// Porter-Duff "over" composite of `src` onto `dst` at canvas offset `(ox, oy)`.
/// Both buffers are sRGB-encoded premultiplied RGBA.
fn composite_over(
dst: &mut [u8], dst_w: u32, dst_h: u32,
src: &[u8], src_w: u32, src_h: u32,
ox: i32, oy: i32,
) {
for row in 0..src_h {
let dy = oy + row as i32;
if dy < 0 || dy >= dst_h as i32 { continue; }
for col in 0..src_w {
let dx = ox + col as i32;
if dx < 0 || dx >= dst_w as i32 { continue; }
let si = ((row * src_w + col) * 4) as usize;
let di = ((dy as u32 * dst_w + dx as u32) * 4) as usize;
let sa = src[si + 3] as u32;
if sa == 0 { continue; }
let da = dst[di + 3] as u32;
// out_a = src_a + dst_a * (255 - src_a) / 255
let out_a = sa + da * (255 - sa) / 255;
dst[di + 3] = out_a as u8;
if out_a > 0 {
for c in 0..3 {
// premul over: out = src + dst*(1-src_a/255)
// v is in [0, 255²], so one /255 brings it back to [0, 255]
let v = src[si + c] as u32 * 255
+ dst[di + c] as u32 * (255 - sa);
dst[di + c] = (v / 255).min(255) as u8;
}
}
}
}
}
/// Commit a floating raster selection: composite it into the keyframe's
/// `raw_pixels` and record a `RasterStrokeAction` for undo.
/// Clears `selection.raster_floating` and `selection.raster_selection`.
@ -2824,7 +2790,7 @@ impl EditorApp {
let canvas_before = kf.raw_pixels.clone();
let canvas_w = kf.width;
let canvas_h = kf.height;
drop(kf); // release immutable borrow before taking mutable
let _ = kf; // release immutable borrow before taking mutable
use lightningbeam_core::selection::{RasterFloatingSelection, RasterSelection};
self.selection.raster_floating = Some(RasterFloatingSelection {
@ -3276,8 +3242,6 @@ impl EditorApp {
has_raster: false,
has_vector: false,
current_time: doc.current_time,
doc_width: doc.width as u32,
doc_height: doc.height as u32,
};
scan(&doc.root.children, &mut h);
h
@ -3636,7 +3600,7 @@ impl EditorApp {
let doc = self.action_executor.document();
let (doc_w, doc_h) = (doc.width as u32, doc.height as u32);
drop(doc);
let _ = doc;
let mut layer = RasterLayer::new(layer_name);
layer.ensure_keyframe_at(self.playback_time, doc_w, doc_h);
let action = lightningbeam_core::actions::AddLayerAction::new(AnyLayer::Raster(layer))
@ -5775,7 +5739,7 @@ impl eframe::App for EditorApp {
.map(|(&lid, _)| lid);
if let Some(layer_id) = recording_layer {
// First, find the clip instance and clip id
let (clip_id, instance_id, timeline_start, trim_start) = {
let (clip_id, instance_id, _timeline_start, _trim_start) = {
let document = self.action_executor.document();
document.get_layer(&layer_id)
.and_then(|layer| {
@ -6551,7 +6515,6 @@ impl eframe::App for EditorApp {
pending_graph_loads: &self.pending_graph_loads,
clipboard_consumed: &mut clipboard_consumed,
keymap: &self.keymap,
commit_raster_floating_if_any: &mut self.commit_raster_floating_if_any,
pending_node_group: &mut self.pending_node_group,
pending_node_ungroup: &mut self.pending_node_ungroup,
#[cfg(debug_assertions)]
@ -7512,7 +7475,7 @@ fn render_pane(
// Active tab highlight with per-corner rounding
if is_active {
let cr = corner_r as u8;
let rounding = egui::Rounding {
let rounding = egui::CornerRadius {
nw: if i == 0 { cr } else { 0 },
sw: if i == 0 { cr } else { 0 },
ne: if i == n - 1 { cr } else { 0 },
@ -7557,7 +7520,7 @@ fn render_pane(
if tab_response.hovered() && !is_active {
ui.painter().rect_filled(
tab_rect,
egui::Rounding {
egui::CornerRadius {
nw: if i == 0 { corner_r as u8 } else { 0 },
sw: if i == 0 { corner_r as u8 } else { 0 },
ne: if i == n - 1 { corner_r as u8 } else { 0 },

View File

@ -238,7 +238,7 @@ pub fn gradient_stop_editor(
// ── Paint handles ─────────────────────────────────────────────────────
// handle_rects was built before any deletions this frame; guard against OOB.
for (i, h_rect) in handle_rects.iter().enumerate().take(gradient.stops.len()) {
let col = ShapeColor_to_Color32(gradient.stops[i].color);
let col = shape_color_to_color32(gradient.stops[i].color);
let is_selected = *selected_stop == Some(i);
let stroke = Stroke::new(
if is_selected { 2.0 } else { 1.0 },
@ -308,7 +308,7 @@ pub fn gradient_stop_editor(
// ── Helpers ──────────────────────────────────────────────────────────────────
fn ShapeColor_to_Color32(c: ShapeColor) -> Color32 {
fn shape_color_to_color32(c: ShapeColor) -> Color32 {
Color32::from_rgba_unmultiplied(c.r, c.g, c.b, c.a)
}

View File

@ -11,7 +11,7 @@
/// - Document settings (when nothing is focused)
use eframe::egui::{self, DragValue, Ui};
use lightningbeam_core::brush_settings::{bundled_brushes, BrushSettings};
use lightningbeam_core::brush_settings::bundled_brushes;
use lightningbeam_core::actions::{SetDocumentPropertiesAction, SetShapePropertiesAction, SetFillPaintAction, SetImageFillAction};
use lightningbeam_core::gradient::ShapeGradient;
use lightningbeam_core::layer::{AnyLayer, LayerTrait};
@ -1439,40 +1439,6 @@ impl InfopanelPane {
}
}
/// Draw a brush dab preview into `rect` approximating the brush falloff shape.
///
/// Renders N concentric filled circles from outermost to innermost. Because each
/// inner circle overwrites the pixels of all outer circles beneath it, the visible
/// alpha at distance `d` from the centre equals the alpha of the innermost circle
/// whose radius ≥ `d`. This step-approximates the actual brush falloff formula:
/// `opa = ((1 r) / (1 hardness))²` for `r > hardness`, 1 inside the hard core.
fn paint_brush_dab(painter: &egui::Painter, rect: egui::Rect, s: &BrushSettings) {
let center = rect.center();
let max_r = (rect.width().min(rect.height()) / 2.0 - 2.0).max(1.0);
let h = s.hardness;
let a = s.opaque;
const N: usize = 12;
for i in 0..N {
// t: normalized radial position of this ring, 1.0 = outermost edge
let t = 1.0 - i as f32 / N as f32;
let r = max_r * t;
let opa_weight = if h >= 1.0 || t <= h {
1.0f32
} else {
let x = (1.0 - t) / (1.0 - h).max(1e-4);
(x * x).min(1.0)
};
let alpha = (opa_weight * a * 220.0).min(220.0) as u8;
painter.circle_filled(
center, r,
egui::Color32::from_rgba_unmultiplied(200, 200, 220, alpha),
);
}
}
/// Convert MIDI note number to note name (e.g. 60 -> "C4")
fn midi_note_name(note: u8) -> String {
const NAMES: [&str; 12] = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];

View File

@ -342,9 +342,6 @@ pub struct SharedPaneState<'a> {
pub clipboard_consumed: &'a mut bool,
/// Remappable keyboard shortcut manager
pub keymap: &'a crate::keymap::KeymapManager,
/// Set by raster selection tools when they need main to commit the floating
/// selection before starting a new interaction.
pub commit_raster_floating_if_any: &'a mut bool,
/// Set by MenuAction::Group when focus is Nodes — consumed by node graph pane
pub pending_node_group: &'a mut bool,
/// Set by MenuAction::Group (ungroup variant) when focus is Nodes — consumed by node graph pane

View File

@ -240,12 +240,6 @@ impl PianoRollPane {
matches!(note % 12, 1 | 3 | 6 | 8 | 10)
}
fn note_name(note: u8) -> String {
let names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
let octave = (note / 12) as i32 - 1;
format!("{}{}", names[note as usize % 12], octave)
}
// ── Note resolution ──────────────────────────────────────────────────
fn resolve_notes(events: &[daw_backend::audio::midi::MidiEvent]) -> Vec<ResolvedNote> {
@ -736,45 +730,6 @@ impl PianoRollPane {
}
}
/// Generate pitch bend MIDI events for a note based on the zone and target semitones.
fn generate_pitch_bend_events(
note_start: f64,
note_duration: f64,
zone: PitchBendZone,
semitones: f32,
channel: u8,
pitch_bend_range: f32,
) -> Vec<daw_backend::audio::midi::MidiEvent> {
use daw_backend::audio::midi::MidiEvent;
let num_steps: usize = 128;
let mut events = Vec::new();
let encode_bend = |normalized: f32| -> (u8, u8) {
let value_14 = (normalized * 8191.0 + 8192.0).clamp(0.0, 16383.0) as i16;
((value_14 & 0x7F) as u8, ((value_14 >> 7) & 0x7F) as u8)
};
// Use t directly (0..=1 across the full note) — same formula as the visual ghost.
// Start: peak → 0 (ramps down over full note)
// Middle: 0 → peak → 0 (sine arch, peaks at center)
// End: 0 → peak (ramps up over full note)
for i in 0..=num_steps {
let t = i as f64 / num_steps as f64;
let t_f32 = t as f32;
// Cosine ease curves: Start+End at equal value = perfectly flat (partition of unity).
// Start: (1+cos(πt))/2 — peaks at t=0, smooth decay to 0 at t=1
// End: (1-cos(πt))/2 — 0 at t=0, smooth rise to peak at t=1
// Middle: sin(πt) — arch peaking at t=0.5
let normalized = match zone {
PitchBendZone::Start => semitones / pitch_bend_range * (1.0 + (std::f32::consts::PI * t_f32).cos()) * 0.5,
PitchBendZone::Middle => semitones / pitch_bend_range * (std::f32::consts::PI * t_f32).sin(),
PitchBendZone::End => semitones / pitch_bend_range * (1.0 - (std::f32::consts::PI * t_f32).cos()) * 0.5,
};
let timestamp = note_start + t * note_duration;
let (lsb, msb) = encode_bend(normalized);
events.push(MidiEvent::new(daw_backend::Beats(timestamp), 0xE0 | channel, lsb, msb));
}
events
}
/// Find the lowest available MIDI channel (115) not already used by any note
/// overlapping [note_start, note_end], excluding the note being assigned itself.
/// Returns the note's current channel unchanged if it is already uniquely assigned (non-zero).
@ -2408,7 +2363,7 @@ impl PaneRenderer for PianoRollPane {
let doc = shared.action_executor.document();
let is_measures = doc.timeline_mode == lightningbeam_core::document::TimelineMode::Measures;
let tempo_map = doc.tempo_map();
drop(doc);
let _ = doc;
if is_measures {
// Auto-detect grid when selection changes

View File

@ -682,17 +682,13 @@ impl egui_wgpu::CallbackTrait for VelloCallback {
if let Some(b_id) = self.ctx.pending_tool_readback_b {
if let Ok(mut gpu_brush) = shared.gpu_brush.lock() {
let dims = gpu_brush.canvases.get(&b_id).map(|c| (c.width, c.height));
if let Some((w, h)) = dims {
if let Some((_w, _h)) = dims {
if let Some(pixels) = gpu_brush.readback_canvas(device, queue, b_id) {
let results = RASTER_READBACK_RESULTS.get_or_init(|| {
Arc::new(Mutex::new(std::collections::HashMap::new()))
});
if let Ok(mut map) = results.lock() {
map.insert(self.ctx.instance_id_for_readback, RasterReadbackResult {
layer_id: uuid::Uuid::nil(), // unused; routing via pending_undo_before
time: 0.0,
canvas_width: w,
canvas_height: h,
pixels,
});
}
@ -763,10 +759,6 @@ impl egui_wgpu::CallbackTrait for VelloCallback {
});
if let Ok(mut map) = results.lock() {
map.insert(self.ctx.instance_id_for_readback, RasterReadbackResult {
layer_id: pending.layer_id,
time: pending.time,
canvas_width: pending.canvas_width,
canvas_height: pending.canvas_height,
pixels,
});
}
@ -3173,7 +3165,6 @@ enum PendingWarpOp {
disp_data: Option<Vec<[f32; 2]>>,
grid_cols: u32,
grid_rows: u32,
w: u32, h: u32,
final_commit: bool,
layer_id: uuid::Uuid,
time: f64,
@ -3190,7 +3181,6 @@ enum PendingWarpOp {
anchor_canvas_id: uuid::Uuid,
disp_buf_id: uuid::Uuid,
display_canvas_id: uuid::Uuid,
w: u32, h: u32,
final_commit: bool,
layer_id: uuid::Uuid,
time: f64,
@ -3251,10 +3241,6 @@ static EYEDROPPER_RESULTS: OnceLock<Arc<Mutex<std::collections::HashMap<u64, (eg
struct PendingRasterDabs {
/// Keyframe UUID — indexes the canvas texture pair in `GpuBrushEngine`.
keyframe_id: uuid::Uuid,
/// Layer UUID — used for the undo readback result.
layer_id: uuid::Uuid,
/// Playback time of the keyframe.
time: f64,
/// Canvas dimensions (pixels).
canvas_width: u32,
canvas_height: u32,
@ -3379,10 +3365,6 @@ static TRANSFORM_READBACK_RESULTS: OnceLock<Arc<Mutex<std::collections::HashMap<
/// Result stored by `prepare()` after a stroke-end readback.
struct RasterReadbackResult {
layer_id: uuid::Uuid,
time: f64,
canvas_width: u32,
canvas_height: u32,
/// Raw RGBA pixels from the completed stroke.
pixels: Vec<u8>,
}
@ -4201,7 +4183,7 @@ impl StagePane {
// Update connected edges: shift the adjacent control point by the same delta
for &edge_id in &connected_edges {
let [v0, v1] = graph.edge(edge_id).vertices;
let [v0, _v1] = graph.edge(edge_id).vertices;
let mut curve = graph.edge(edge_id).curve;
if v0 == vertex_id {
@ -5609,58 +5591,6 @@ impl StagePane {
}
}
/// Lift the pixels enclosed by the current `raster_selection` into a
/// `RasterFloatingSelection`, punching a transparent hole in `raw_pixels`.
///
/// Call this immediately after a marquee / lasso selection is finalized so
/// that all downstream operations (drag-move, copy, cut, stroke-masking)
/// see a consistent `raster_floating` whenever a selection is active.
/// Build an R8 mask buffer (0 = outside, 255 = inside) from a selection.
fn build_selection_mask(
sel: &lightningbeam_core::selection::RasterSelection,
width: u32,
height: u32,
) -> Vec<u8> {
let mut mask = vec![0u8; (width * height) as usize];
let (x0, y0, x1, y1) = sel.bounding_rect();
let bx0 = x0.max(0) as u32;
let by0 = y0.max(0) as u32;
let bx1 = (x1 as u32).min(width);
let by1 = (y1 as u32).min(height);
for y in by0..by1 {
for x in bx0..bx1 {
if sel.contains_pixel(x as i32, y as i32) {
mask[(y * width + x) as usize] = 255;
}
}
}
mask
}
/// Build an R8 mask buffer for the float canvas (0 = outside selection, 255 = inside).
/// Coordinates are in float-local space: pixel (fx, fy) corresponds to document pixel
/// (float_x+fx, float_y+fy).
fn build_float_mask(
sel: &lightningbeam_core::selection::RasterSelection,
float_x: i32, float_y: i32,
float_w: u32, float_h: u32,
) -> Vec<u8> {
let mut mask = vec![0u8; (float_w * float_h) as usize];
let (x0, y0, x1, y1) = sel.bounding_rect();
let bx0 = (x0 - float_x).max(0) as u32;
let by0 = (y0 - float_y).max(0) as u32;
let bx1 = ((x1 - float_x) as u32).min(float_w);
let by1 = ((y1 - float_y) as u32).min(float_h);
for fy in by0..by1 {
for fx in bx0..bx1 {
if sel.contains_pixel(float_x + fx as i32, float_y + fy as i32) {
mask[(fy * float_w + fx) as usize] = 255;
}
}
}
mask
}
/// Allocate the three A/B/C GPU canvases and build a [`crate::raster_tool::RasterWorkspace`]
/// for a new raster tool operation.
///
@ -5699,7 +5629,6 @@ impl StagePane {
a_canvas_id: a_id,
b_canvas_id: b_id,
c_canvas_id: c_id,
mask_texture: None,
width: w,
height: h,
x,
@ -5724,7 +5653,7 @@ impl StagePane {
let layer_id = (*shared.active_layer_id)?;
let time = *shared.playback_time;
let (doc_w, doc_h) = {
let (_doc_w, _doc_h) = {
let doc = shared.action_executor.document();
(doc.width as u32, doc.height as u32)
};
@ -5733,7 +5662,7 @@ impl StagePane {
// returns None (no workspace) if there's no active keyframe to lift from.
// Read keyframe id and pixels.
let (kf_id, w, h, pixels) = {
let (_kf_id, w, h, pixels) = {
let doc = shared.action_executor.document();
let AnyLayer::Raster(rl) = doc.get_layer(&layer_id)? else { return None };
let kf = rl.keyframe_at(time)?;
@ -5753,7 +5682,6 @@ impl StagePane {
a_canvas_id: a_id,
b_canvas_id: b_id,
c_canvas_id: c_id,
mask_texture: None,
width: w,
height: h,
x: 0,
@ -5761,9 +5689,6 @@ impl StagePane {
source: WorkspaceSource::Layer {
layer_id,
time,
kf_id,
canvas_w: doc_w,
canvas_h: doc_h,
},
before_pixels: pixels.clone(),
};
@ -6135,8 +6060,6 @@ impl StagePane {
));
self.pending_raster_dabs = Some(PendingRasterDabs {
keyframe_id: canvas_id,
layer_id,
time,
canvas_width,
canvas_height,
initial_pixels: None, // canvas already initialized via lazy GPU init
@ -6220,8 +6143,6 @@ impl StagePane {
));
self.pending_raster_dabs = Some(PendingRasterDabs {
keyframe_id,
layer_id: active_layer_id,
time: kf_time,
canvas_width,
canvas_height,
initial_pixels: Some(initial_pixels),
@ -6299,8 +6220,6 @@ impl StagePane {
let (dabs, dab_bbox) = BrushEngine::compute_dabs(&seg, stroke_state, dt);
self.pending_raster_dabs = Some(PendingRasterDabs {
keyframe_id: canvas_id,
layer_id,
time,
canvas_width: cw,
canvas_height: ch,
initial_pixels: None,
@ -6363,8 +6282,6 @@ impl StagePane {
if !dabs.is_empty() {
self.pending_raster_dabs = Some(PendingRasterDabs {
keyframe_id: canvas_id,
layer_id,
time,
canvas_width: cw,
canvas_height: ch,
initial_pixels: None,
@ -6424,8 +6341,6 @@ impl StagePane {
if let Some(kf_id) = kf_id {
self.pending_raster_dabs = Some(PendingRasterDabs {
keyframe_id: kf_id,
layer_id: ub_layer,
time: ub_time,
canvas_width: ub_cw,
canvas_height: ub_ch,
initial_pixels: None,
@ -6941,7 +6856,7 @@ impl StagePane {
fn handle_quick_select_tool(
&mut self,
ui: &mut egui::Ui,
_ui: &mut egui::Ui,
response: &egui::Response,
world_pos: egui::Vec2,
shared: &mut SharedPaneState,
@ -8431,7 +8346,6 @@ impl StagePane {
world_pos: egui::Vec2,
shared: &mut SharedPaneState,
) {
use lightningbeam_core::tool::Tool;
use uuid::Uuid;
// Ensure we're on a raster layer.
@ -8469,7 +8383,6 @@ impl StagePane {
disp_data: Some(disp_data),
grid_cols: ws.grid_cols,
grid_rows: ws.grid_rows,
w: ws.anchor_w, h: ws.anchor_h,
final_commit: true,
layer_id: ws.layer_id,
time: ws.time,
@ -8695,7 +8608,6 @@ impl StagePane {
disp_data: Some(disp_data),
grid_cols: ws.grid_cols,
grid_rows: ws.grid_rows,
w: ws.anchor_w, h: ws.anchor_h,
final_commit: false,
layer_id: ws.layer_id,
time: ws.time,
@ -8705,7 +8617,7 @@ impl StagePane {
None
};
let (ws_layer_id, ws_display_id, ws_float_offset) = (ws.layer_id, ws.display_canvas_id, ws.float_offset);
drop(ws); // release borrow of warp_state
let _ = ws; // release borrow of warp_state
// Display canvas is initialised by Init (zero-displacement apply), so it always
// has valid content. For full-layer warp, override the layer blit unconditionally.
@ -8778,7 +8690,6 @@ impl StagePane {
anchor_canvas_id: ls.anchor_canvas_id,
disp_buf_id: ls.disp_buf_id,
display_canvas_id: ls.display_canvas_id,
w: ls.anchor_w, h: ls.anchor_h,
final_commit: true,
layer_id: ls.layer_id,
time: ls.time,
@ -8950,7 +8861,6 @@ impl StagePane {
anchor_canvas_id: anchor_id,
disp_buf_id: disp_buf,
display_canvas_id: display_id,
w, h,
final_commit: false,
layer_id: ls_layer_id,
time,
@ -9013,7 +8923,7 @@ impl StagePane {
} else { None }
} else { None }
} else { None };
drop(doc);
let _ = doc;
r
} else { None };
@ -10706,7 +10616,6 @@ impl StagePane {
// Alt+click: set source point for clone/healing tools.
{
use lightningbeam_core::tool::Tool;
let tool_uses_alt = crate::tools::raster_tool_def(shared.selected_tool)
.map_or(false, |d| d.uses_alt_click());
if tool_uses_alt
@ -11602,7 +11511,6 @@ impl PaneRenderer for StagePane {
disp_data: Some(disp_data),
grid_cols: ws.grid_cols,
grid_rows: ws.grid_rows,
w: ws.anchor_w, h: ws.anchor_h,
final_commit: true,
layer_id: ws.layer_id,
time: ws.time,
@ -11623,7 +11531,6 @@ impl PaneRenderer for StagePane {
anchor_canvas_id: ls.anchor_canvas_id,
disp_buf_id: ls.disp_buf_id,
display_canvas_id: ls.display_canvas_id,
w: ls.anchor_w, h: ls.anchor_h,
final_commit: true,
layer_id: ls.layer_id,
time: ls.time,

View File

@ -483,7 +483,6 @@ struct AutomationLaneRender {
value_max: f32,
accent_color: egui::Color32,
playback_time: f64,
kind: AutomationLaneKind,
}
/// Pending automation keyframe edit action from curve lane interaction
@ -1160,7 +1159,7 @@ impl TimelinePane {
*shared.recording_clips.get(&layer_id).unwrap_or(&0), daw_backend::Beats::ZERO);
let doc_clip_id = shared.action_executor.document_mut().add_audio_clip(doc_clip);
let mut clip_instance = ClipInstance::new(doc_clip_id)
let clip_instance = ClipInstance::new(doc_clip_id)
.with_timeline_start(start_time);
if let Some(layer) = shared.action_executor.document_mut().get_layer_mut(&layer_id) {
@ -3230,7 +3229,6 @@ impl TimelinePane {
value_max: lane.value_max,
accent_color: lane_accent,
playback_time,
kind: lane.kind,
});
}
}
@ -4054,7 +4052,6 @@ impl TimelinePane {
value_max: lane.value_max,
accent_color: lane_accent,
playback_time,
kind: lane.kind,
});
}
}
@ -5409,12 +5406,10 @@ impl PaneRenderer for TimelinePane {
let editing_clip_id = shared.editing_clip_id;
let mut context_layers = document.context_layers(editing_clip_id.as_ref());
// Prepend master track as the first row when enabled (only at root context, not inside clips)
let master_any_layer;
let _master_any_layer;
if self.show_master_track && editing_clip_id.is_none() {
master_any_layer = Some(AnyLayer::Group(document.master_layer.clone()));
context_layers.insert(0, master_any_layer.as_ref().unwrap());
} else {
master_any_layer = None;
_master_any_layer = Some(AnyLayer::Group(document.master_layer.clone()));
context_layers.insert(0, _master_any_layer.as_ref().unwrap());
}
// Use virtual row count (includes expanded group children) for height calculations
let layer_count = build_timeline_rows(&context_layers).len();

View File

@ -12,7 +12,6 @@
//! allocates and validates them in [`begin_raster_workspace`]; tools only
//! dispatch shaders.
use std::sync::Arc;
use uuid::Uuid;
use eframe::egui;
@ -25,11 +24,6 @@ pub enum WorkspaceSource {
Layer {
layer_id: Uuid,
time: f64,
/// The keyframe's own UUID (the A-canvas key in `GpuBrushEngine`).
kf_id: Uuid,
/// Full canvas dimensions (may differ from workspace dims for floating selections).
canvas_w: u32,
canvas_h: u32,
},
/// Operating on the floating selection.
Float,
@ -51,9 +45,6 @@ pub struct RasterWorkspace {
pub b_canvas_id: Uuid,
/// C canvas (Rgba16Float) — scratch; tools accumulate dabs here across the stroke.
pub c_canvas_id: Uuid,
/// Optional R8Unorm selection mask (same pixel dimensions as A/B/C).
/// `None` means the entire workspace is selected.
pub mask_texture: Option<Arc<wgpu::Texture>>,
/// Pixel dimensions. A, B, C, and mask are all guaranteed to be this size.
pub width: u32,
pub height: u32,
@ -69,40 +60,6 @@ pub struct RasterWorkspace {
}
impl RasterWorkspace {
/// Panic-safe bounds check. Asserts that every GPU canvas exists and has
/// the dimensions declared by this workspace. Called by the framework
/// before `begin()` and before each `update()`.
pub fn validate(&self, gpu: &crate::gpu_brush::GpuBrushEngine) {
for (name, id) in [
("A", self.a_canvas_id),
("B", self.b_canvas_id),
("C", self.c_canvas_id),
] {
let canvas = gpu.canvases.get(&id).unwrap_or_else(|| {
panic!(
"RasterWorkspace::validate: buffer '{}' (id={}) not found in GpuBrushEngine",
name, id
)
});
assert_eq!(
canvas.width, self.width,
"RasterWorkspace::validate: buffer '{}' width {} != workspace width {}",
name, canvas.width, self.width
);
assert_eq!(
canvas.height, self.height,
"RasterWorkspace::validate: buffer '{}' height {} != workspace height {}",
name, canvas.height, self.height
);
}
let expected = (self.width * self.height * 4) as usize;
assert_eq!(
self.before_pixels.len(), expected,
"RasterWorkspace::validate: before_pixels.len()={} != expected {}",
self.before_pixels.len(), expected
);
}
/// Returns the three canvas UUIDs as an array (convenient for bulk removal).
pub fn canvas_ids(&self) -> [Uuid; 3] {
[self.a_canvas_id, self.b_canvas_id, self.c_canvas_id]
@ -208,11 +165,6 @@ pub trait RasterTool: Send + Sync {
/// the operation was a no-op (e.g. the pointer never moved).
fn finish(&mut self, ws: &RasterWorkspace) -> bool;
/// Called on **Escape** or tool switch mid-stroke. The caller restores the
/// source pixels from `ws.before_pixels` without creating an undo entry; the
/// tool just cleans up internal state.
fn cancel(&mut self, ws: &RasterWorkspace);
/// Called once per frame (in the VelloCallback construction, UI thread) to
/// extract pending GPU work accumulated by `begin()` / `update()`.
///
@ -377,384 +329,7 @@ impl RasterTool for BrushRasterTool {
self.has_dabs
}
fn cancel(&mut self, _ws: &RasterWorkspace) {
self.pending = None;
self.has_dabs = false;
}
fn take_pending_gpu_work(&mut self) -> Option<Box<dyn PendingGpuWork>> {
self.pending.take().map(|w| w as Box<dyn PendingGpuWork>)
}
}
// ── EffectBrushTool ───────────────────────────────────────────────────────────
/// Raster tool for effect brushes (Blur, Sharpen, Dodge, Burn, Sponge, Desaturate).
///
/// C accumulates a per-pixel influence weight (R channel, 0255).
/// The composite pass applies the effect to A, scaled by C.r, writing to B:
/// `B = lerp(A, effect(A), C.r)`
///
/// Using C as an influence map (rather than accumulating modified pixels) prevents
/// overlapping dabs from compounding the effect beyond the C.r cap (255).
///
/// # GPU implementation (TODO)
/// Requires a dedicated `effect_brush_composite.wgsl` shader that reads A and C,
/// applies the blend-mode-specific filter to A, and blends by C.r → B.
pub struct EffectBrushTool {
brush: BrushSettings,
blend_mode: RasterBlendMode,
has_dabs: bool,
}
impl EffectBrushTool {
pub fn new(brush: BrushSettings, blend_mode: RasterBlendMode) -> Self {
Self { brush, blend_mode, has_dabs: false }
}
}
impl RasterTool for EffectBrushTool {
fn begin(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.has_dabs = true; // placeholder
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dabs }
fn cancel(&mut self, _ws: &RasterWorkspace) { self.has_dabs = false; }
// GPU shaders not yet implemented; take_pending_gpu_work returns None (default).
}
// ── SmudgeTool ────────────────────────────────────────────────────────────────
/// Raster tool for the smudge brush.
///
/// `begin()`: copy A → C so C starts with the source pixels for color pickup.
/// `update()`: dispatch smudge dabs using `blend_mode=2` (reads C as source,
/// writes smear to C); then composite C over A → B.
/// Because the smudge shader reads from `canvas_src` (C.src) and writes to
/// `canvas_dst` (C.dst), existing dabs are preserved in the smear history.
///
/// # GPU implementation (TODO)
/// Requires an initial A → C copy in `begin()` (via GPU copy command).
/// The smudge dab dispatch then uses `render_dabs(c_id, smudge_dabs, ...)`.
/// The composite pass is `composite_a_c_to_b` (same as BrushRasterTool).
pub struct SmudgeTool {
brush: BrushSettings,
has_dabs: bool,
}
impl SmudgeTool {
pub fn new(brush: BrushSettings) -> Self {
Self { brush, has_dabs: false }
}
}
impl RasterTool for SmudgeTool {
fn begin(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.has_dabs = true; // placeholder
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dabs }
fn cancel(&mut self, _ws: &RasterWorkspace) { self.has_dabs = false; }
// GPU shaders not yet implemented; take_pending_gpu_work returns None (default).
}
// ── GradientRasterTool ────────────────────────────────────────────────────────
use crate::gpu_brush::GpuGradientStop;
use lightningbeam_core::gradient::{GradientExtend, GradientType, ShapeGradient};
fn gradient_stops_to_gpu(gradient: &ShapeGradient) -> Vec<GpuGradientStop> {
gradient.stops.iter().map(|s| {
GpuGradientStop::from_srgb_u8(s.position, s.color.r, s.color.g, s.color.b, s.color.a)
}).collect()
}
fn gradient_extend_to_u32(extend: GradientExtend) -> u32 {
match extend {
GradientExtend::Pad => 0,
GradientExtend::Reflect => 1,
GradientExtend::Repeat => 2,
}
}
fn gradient_kind_to_u32(kind: GradientType) -> u32 {
match kind {
GradientType::Linear => 0,
GradientType::Radial => 1,
}
}
struct PendingGradientWork {
a_id: Uuid,
b_id: Uuid,
stops: Vec<GpuGradientStop>,
start: (f32, f32),
end: (f32, f32),
opacity: f32,
extend_mode: u32,
kind: u32,
}
impl PendingGpuWork for PendingGradientWork {
fn execute(&self, device: &wgpu::Device, queue: &wgpu::Queue, gpu: &mut crate::gpu_brush::GpuBrushEngine) {
gpu.apply_gradient_fill(
device, queue,
&self.a_id, &self.b_id,
&self.stops,
self.start, self.end,
self.opacity, self.extend_mode, self.kind,
);
}
}
/// Raster tool for gradient fills.
///
/// `begin()` records the canvas-local start position.
/// `update()` recomputes gradient parameters from settings and queues a
/// `PendingGradientWork` that calls `apply_gradient_fill` in `prepare()`.
/// `finish()` returns whether any gradient was dispatched.
pub struct GradientRasterTool {
start_canvas: egui::Vec2,
end_canvas: egui::Vec2,
pending: Option<Box<PendingGradientWork>>,
has_dispatched: bool,
}
impl GradientRasterTool {
pub fn new() -> Self {
Self {
start_canvas: egui::Vec2::ZERO,
end_canvas: egui::Vec2::ZERO,
pending: None,
has_dispatched: false,
}
}
}
impl RasterTool for GradientRasterTool {
fn begin(&mut self, ws: &RasterWorkspace, pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
let canvas_pos = pos - egui::vec2(ws.x as f32, ws.y as f32);
self.start_canvas = canvas_pos;
self.end_canvas = canvas_pos;
}
fn update(&mut self, ws: &RasterWorkspace, pos: egui::Vec2, _dt: f32, settings: &crate::tools::RasterToolSettings) {
self.end_canvas = pos - egui::vec2(ws.x as f32, ws.y as f32);
let gradient = &settings.gradient;
self.pending = Some(Box::new(PendingGradientWork {
a_id: ws.a_canvas_id,
b_id: ws.b_canvas_id,
stops: gradient_stops_to_gpu(gradient),
start: (self.start_canvas.x, self.start_canvas.y),
end: (self.end_canvas.x, self.end_canvas.y),
opacity: settings.gradient_opacity,
extend_mode: gradient_extend_to_u32(gradient.extend),
kind: gradient_kind_to_u32(gradient.kind),
}));
self.has_dispatched = true;
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dispatched }
fn cancel(&mut self, _ws: &RasterWorkspace) {
self.pending = None;
self.has_dispatched = false;
}
fn take_pending_gpu_work(&mut self) -> Option<Box<dyn PendingGpuWork>> {
self.pending.take().map(|w| w as Box<dyn PendingGpuWork>)
}
}
// ── TransformRasterTool ───────────────────────────────────────────────────────
use crate::gpu_brush::RasterTransformGpuParams;
struct PendingTransformWork {
a_id: Uuid,
b_id: Uuid,
params: RasterTransformGpuParams,
}
impl PendingGpuWork for PendingTransformWork {
fn execute(&self, device: &wgpu::Device, queue: &wgpu::Queue, gpu: &mut crate::gpu_brush::GpuBrushEngine) {
gpu.render_transform(device, queue, &self.a_id, &self.b_id, self.params);
}
}
/// Raster tool for affine transforms (move, scale, rotate, shear).
///
/// `begin()` stores the initial canvas dimensions and queues an identity
/// transform so B is initialised on the first frame.
/// `update()` recomputes the inverse affine matrix from the current handle
/// positions and queues a new `PendingTransformWork`.
///
/// The inverse matrix maps output pixel coordinates back to source pixel
/// coordinates: `src = M_inv * dst + b`
/// where `M_inv = [[a00, a01], [a10, a11]]` and `b = [b0, b1]`.
///
/// # GPU implementation
/// Fully wired — uses `GpuBrushEngine::render_transform`. Handle interaction
/// logic (drag, rotate, scale) is handled by the tool's `update()` caller in
/// `stage.rs` which computes and passes in the `RasterTransformGpuParams`.
pub struct TransformRasterTool {
pending: Option<Box<PendingTransformWork>>,
has_dispatched: bool,
canvas_w: u32,
canvas_h: u32,
}
impl TransformRasterTool {
pub fn new() -> Self {
Self {
pending: None,
has_dispatched: false,
canvas_w: 0,
canvas_h: 0,
}
}
/// Queue a transform with the given inverse-affine matrix.
/// Called by the stage handler after computing handle positions.
pub fn set_transform(
&mut self,
ws: &RasterWorkspace,
params: RasterTransformGpuParams,
) {
self.pending = Some(Box::new(PendingTransformWork {
a_id: ws.a_canvas_id,
b_id: ws.b_canvas_id,
params,
}));
self.has_dispatched = true;
}
}
impl RasterTool for TransformRasterTool {
fn begin(&mut self, ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.canvas_w = ws.width;
self.canvas_h = ws.height;
// Queue identity transform so B shows the source immediately.
let identity = RasterTransformGpuParams {
a00: 1.0, a01: 0.0,
a10: 0.0, a11: 1.0,
b0: 0.0, b1: 0.0,
src_w: ws.width, src_h: ws.height,
dst_w: ws.width, dst_h: ws.height,
_pad0: 0, _pad1: 0,
};
self.set_transform(ws, identity);
}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
// Handle interaction and matrix updates are driven from stage.rs via set_transform().
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dispatched }
fn cancel(&mut self, _ws: &RasterWorkspace) {
self.pending = None;
self.has_dispatched = false;
}
fn take_pending_gpu_work(&mut self) -> Option<Box<dyn PendingGpuWork>> {
self.pending.take().map(|w| w as Box<dyn PendingGpuWork>)
}
}
// ── WarpRasterTool ────────────────────────────────────────────────────────────
/// Raster tool for warp / mesh deformation.
///
/// Uses a displacement buffer (managed by `GpuBrushEngine`) that maps each
/// output pixel to a source offset. The displacement grid is updated by
/// dragging control points; the warp shader reads anchor pixels + displacement
/// → B each frame.
///
/// # GPU implementation (TODO)
/// Requires: `create_displacement_buf`, `apply_warp` already exist in
/// `GpuBrushEngine`. Wire brush-drag interaction to update displacement
/// entries and call `apply_warp`.
pub struct WarpRasterTool {
has_dispatched: bool,
}
impl WarpRasterTool {
pub fn new() -> Self { Self { has_dispatched: false } }
}
impl RasterTool for WarpRasterTool {
fn begin(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.has_dispatched = true; // placeholder
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dispatched }
fn cancel(&mut self, _ws: &RasterWorkspace) { self.has_dispatched = false; }
// take_pending_gpu_work: default (None) — full GPU wiring is TODO.
}
// ── LiquifyRasterTool ─────────────────────────────────────────────────────────
/// Raster tool for liquify (per-pixel displacement painting).
///
/// Similar to `WarpRasterTool` but uses a full per-pixel displacement map
/// (grid_cols = grid_rows = 0 in `apply_warp`) painted by brush strokes.
/// Each dab accumulates displacement in the push/pull/swirl direction.
///
/// # GPU implementation (TODO)
/// Requires: a dab-to-displacement shader that accumulates per-pixel offsets
/// into the displacement buffer, then `apply_warp` reads it → B.
pub struct LiquifyRasterTool {
has_dispatched: bool,
}
impl LiquifyRasterTool {
pub fn new() -> Self { Self { has_dispatched: false } }
}
impl RasterTool for LiquifyRasterTool {
fn begin(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.has_dispatched = true; // placeholder
}
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { self.has_dispatched }
fn cancel(&mut self, _ws: &RasterWorkspace) { self.has_dispatched = false; }
// take_pending_gpu_work: default (None) — full GPU wiring is TODO.
}
// ── SelectionTool ─────────────────────────────────────────────────────────────
/// Raster selection tool (Magic Wand / Quick Select).
///
/// C (RGBA8) acts as the growing selection; C.r = mask value (0 or 255).
/// Each `update()` frame a flood-fill / region-grow shader extends C.r.
/// The composite pass draws A + a tinted overlay from C.r → B so the user
/// sees the growing selection boundary.
///
/// `finish()` returns false (commit does not write pixels back to the layer;
/// instead the caller extracts C.r into the standalone `R8Unorm` selection
/// texture via `shared.raster_selection`).
///
/// # GPU implementation (TODO)
/// Requires: a flood-fill compute shader seeded by the click position that
/// grows the selection in C.r; and a composite shader that tints selected
/// pixels blue/cyan for preview.
pub struct SelectionTool {
has_selection: bool,
}
impl SelectionTool {
pub fn new() -> Self { Self { has_selection: false } }
}
impl RasterTool for SelectionTool {
fn begin(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {}
fn update(&mut self, _ws: &RasterWorkspace, _pos: egui::Vec2, _dt: f32, _settings: &crate::tools::RasterToolSettings) {
self.has_selection = true; // placeholder
}
/// Selection tools never trigger a pixel readback/commit on mouseup.
/// The caller reads C.r directly into the selection mask texture.
fn finish(&mut self, _ws: &RasterWorkspace) -> bool { false }
fn cancel(&mut self, _ws: &RasterWorkspace) { self.has_selection = false; }
// take_pending_gpu_work: default (None) — full GPU wiring is TODO.
}

View File

@ -232,15 +232,6 @@ impl TestModeState {
}
}
/// Store geometry context for panic capture.
/// Called before risky operations (e.g. region select) so the panic hook
/// can include it in the crash file for easier reproduction.
pub fn set_pending_geometry(&self, context: serde_json::Value) {
if let Ok(mut guard) = self.pending_geometry.try_lock() {
*guard = Some(context);
}
}
/// Clear the pending geometry context (call after the operation succeeds).
pub fn clear_pending_geometry(&self) {
if let Ok(mut guard) = self.pending_geometry.try_lock() {

View File

@ -42,6 +42,7 @@ impl ThemeMode {
/// Background type for CSS backgrounds
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum Background {
Solid(egui::Color32),
LinearGradient {
@ -456,6 +457,7 @@ impl Theme {
}
/// Invalidate the cache (call on stylesheet reload or mode change)
#[allow(dead_code)]
pub fn invalidate_cache(&self) {
self.cache.borrow_mut().clear();
}
@ -518,6 +520,7 @@ impl Theme {
}
/// Convenience: resolve and extract a dimension with fallback
#[allow(dead_code)]
pub fn dimension(&self, context: &[&str], ctx: &egui::Context, property: &str, fallback: f32) -> f32 {
let style = self.resolve(context, ctx);
match property {
@ -534,6 +537,7 @@ impl Theme {
}
/// Paint background for a region (handles solid/gradient/image)
#[allow(dead_code)]
pub fn paint_bg(
&self,
context: &[&str],

View File

@ -7,6 +7,7 @@ use eframe::egui;
use crate::theme::Background;
/// Paint a background into the given rect
#[allow(dead_code)]
pub fn paint_background(
painter: &egui::Painter,
rect: egui::Rect,
@ -35,6 +36,7 @@ pub fn paint_background(
/// - 90deg = left to right
/// - 180deg = top to bottom (default)
/// - 270deg = right to left
#[allow(dead_code)]
pub fn paint_linear_gradient(
painter: &egui::Painter,
rect: egui::Rect,