mirror of https://codeberg.org/topola/topola.git
Add appearance panel, show only enabled layers
This commit is contained in:
parent
8a50e70a6a
commit
fde7b163df
|
|
@ -128,6 +128,10 @@ impl eframe::App for App {
|
|||
|
||||
self.update_state();
|
||||
|
||||
if let Some(ref mut workspace) = self.workspace {
|
||||
workspace.update_appearance_panel(ctx);
|
||||
}
|
||||
|
||||
self.viewport.update(ctx, self.workspace.as_mut());
|
||||
|
||||
self.update_locale();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,217 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use egui::{Context, Grid, ScrollArea, SidePanel, widget_text::WidgetText};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use topola::Board;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Colors {
|
||||
pub layers: LayerColors,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct LayerColors {
|
||||
default: LayerColor,
|
||||
colors: BTreeMap<String, LayerColor>,
|
||||
}
|
||||
|
||||
impl LayerColors {
|
||||
pub fn color(&self, layer_name: Option<&str>) -> &LayerColor {
|
||||
layer_name
|
||||
.map(|layername| self.colors.get(layername).unwrap_or(&self.default))
|
||||
.unwrap_or(&self.default)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct LayerColor {
|
||||
pub normal: egui::Color32,
|
||||
pub highlighted: egui::Color32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct AppearancePanel {
|
||||
// TODO:
|
||||
// In1.Cu shall be #7fc87f (#d5ecd5 when selected).
|
||||
// In2.Cu shall be #ce7d2c (#e8c39e when selected).
|
||||
pub dark_colors: Colors,
|
||||
pub light_colors: Colors,
|
||||
|
||||
#[serde(skip)]
|
||||
pub visible: Box<[bool]>,
|
||||
}
|
||||
|
||||
impl AppearancePanel {
|
||||
pub fn new(board: &Board) -> Self {
|
||||
let dark_colors = Colors {
|
||||
layers: LayerColors {
|
||||
default: LayerColor {
|
||||
normal: egui::Color32::from_rgb(255, 255, 255),
|
||||
highlighted: egui::Color32::from_rgb(255, 255, 255),
|
||||
},
|
||||
colors: BTreeMap::from([
|
||||
(
|
||||
"F.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(255, 52, 52),
|
||||
highlighted: egui::Color32::from_rgb(255, 100, 100),
|
||||
},
|
||||
),
|
||||
(
|
||||
"1".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(255, 52, 52),
|
||||
highlighted: egui::Color32::from_rgb(255, 100, 100),
|
||||
},
|
||||
),
|
||||
(
|
||||
"B.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(52, 52, 255),
|
||||
highlighted: egui::Color32::from_rgb(100, 100, 255),
|
||||
},
|
||||
),
|
||||
(
|
||||
"2".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(52, 52, 255),
|
||||
highlighted: egui::Color32::from_rgb(100, 100, 255),
|
||||
},
|
||||
),
|
||||
(
|
||||
"In1.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(127, 200, 127),
|
||||
highlighted: egui::Color32::from_rgb(213, 236, 213),
|
||||
},
|
||||
),
|
||||
(
|
||||
"In2.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(206, 125, 44),
|
||||
highlighted: egui::Color32::from_rgb(232, 195, 158),
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
};
|
||||
let light_colors = Colors {
|
||||
layers: LayerColors {
|
||||
default: LayerColor {
|
||||
normal: egui::Color32::from_rgb(0, 0, 0),
|
||||
highlighted: egui::Color32::from_rgb(0, 0, 0),
|
||||
},
|
||||
colors: BTreeMap::from([
|
||||
(
|
||||
"F.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(255, 27, 27),
|
||||
highlighted: egui::Color32::from_rgb(255, 52, 52),
|
||||
},
|
||||
),
|
||||
(
|
||||
"1".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(255, 27, 27),
|
||||
highlighted: egui::Color32::from_rgb(255, 52, 52),
|
||||
},
|
||||
),
|
||||
(
|
||||
"B.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(27, 27, 255),
|
||||
highlighted: egui::Color32::from_rgb(52, 52, 255),
|
||||
},
|
||||
),
|
||||
(
|
||||
"2".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(27, 27, 255),
|
||||
highlighted: egui::Color32::from_rgb(52, 52, 255),
|
||||
},
|
||||
),
|
||||
(
|
||||
"In1.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(76, 169, 76),
|
||||
highlighted: egui::Color32::from_rgb(127, 200, 127),
|
||||
},
|
||||
),
|
||||
(
|
||||
"In2.Cu".to_string(),
|
||||
LayerColor {
|
||||
normal: egui::Color32::from_rgb(183, 80, 12),
|
||||
highlighted: egui::Color32::from_rgb(206, 125, 44),
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let layer_count = board.layout().layer_count();
|
||||
let visible = core::iter::repeat(true)
|
||||
.take(*layer_count)
|
||||
.collect::<Box<[_]>>();
|
||||
Self {
|
||||
dark_colors,
|
||||
light_colors,
|
||||
visible,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ctx: &Context, board: &Board) {
|
||||
SidePanel::right("appearance_panel").show(ctx, |ui| {
|
||||
ui.label("Layers");
|
||||
let row_height = ui.spacing().interact_size.y;
|
||||
ScrollArea::vertical().show_rows(
|
||||
ui,
|
||||
row_height,
|
||||
self.visible.len(),
|
||||
|ui, row_range| {
|
||||
let start_row = row_range.start;
|
||||
Grid::new("appearance_layers")
|
||||
.min_col_width(ui.spacing().icon_width)
|
||||
.num_columns(3)
|
||||
.start_row(start_row)
|
||||
.show(ui, |ui| {
|
||||
for (layer, visible) in self.visible[row_range].iter_mut().enumerate() {
|
||||
let layer = layer + start_row;
|
||||
let layer_name = board.layer_name(layer);
|
||||
|
||||
// unnamed layers can't be used for routing
|
||||
/*if layer_name.is_some() {
|
||||
ui.radio_value(
|
||||
&mut options.planar.principal_layer,
|
||||
layer,
|
||||
WidgetText::default(),
|
||||
);
|
||||
} else {
|
||||
// dummy item to bump the grid
|
||||
ui.label("");
|
||||
}*/
|
||||
|
||||
ui.checkbox(visible, WidgetText::default());
|
||||
ui.label(
|
||||
layer_name
|
||||
.map(|i| i.to_string())
|
||||
.unwrap_or_else(|| format!("{} - Unnamed layer", layer)),
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn colors(&self, ctx: &Context) -> &Colors {
|
||||
match ctx.theme() {
|
||||
egui::Theme::Dark => &self.dark_colors,
|
||||
egui::Theme::Light => &self.light_colors,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,24 +46,58 @@ impl Displayer {
|
|||
);
|
||||
|
||||
for (_, joint) in workspace.board.layout().joints().collection() {
|
||||
self.paint_joint(ctx, ui, viewport, joint);
|
||||
if workspace.appearance_panel.visible[joint.layer] {
|
||||
self.paint_joint(
|
||||
ctx,
|
||||
ui,
|
||||
viewport,
|
||||
joint,
|
||||
workspace
|
||||
.appearance_panel
|
||||
.colors(ctx)
|
||||
.layers
|
||||
.color(workspace.board.layer_name(joint.layer))
|
||||
.normal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (i, segment) in workspace.board.layout().segments().collection() {
|
||||
self.paint_segment(
|
||||
ctx,
|
||||
ui,
|
||||
viewport,
|
||||
segment,
|
||||
workspace
|
||||
.board
|
||||
.layout()
|
||||
.segment_endpoints(SegmentId::new(i)),
|
||||
);
|
||||
if workspace.appearance_panel.visible[segment.layer] {
|
||||
self.paint_segment(
|
||||
ctx,
|
||||
ui,
|
||||
viewport,
|
||||
segment,
|
||||
workspace
|
||||
.board
|
||||
.layout()
|
||||
.segment_endpoints(SegmentId::new(i)),
|
||||
workspace
|
||||
.appearance_panel
|
||||
.colors(ctx)
|
||||
.layers
|
||||
.color(workspace.board.layer_name(segment.layer))
|
||||
.normal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (_, polygon) in workspace.board.layout().polygons().collection() {
|
||||
self.paint_polygon(ctx, ui, viewport, polygon);
|
||||
if workspace.appearance_panel.visible[polygon.layer] {
|
||||
self.paint_polygon(
|
||||
ctx,
|
||||
ui,
|
||||
viewport,
|
||||
polygon,
|
||||
workspace
|
||||
.appearance_panel
|
||||
.colors(ctx)
|
||||
.layers
|
||||
.color(workspace.board.layer_name(polygon.layer))
|
||||
.normal,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,11 +107,12 @@ impl Displayer {
|
|||
ui: &egui::Ui,
|
||||
viewport: &Viewport,
|
||||
joint: &Joint,
|
||||
color: egui::Color32,
|
||||
) {
|
||||
ui.painter().circle_filled(
|
||||
egui::pos2(joint.position[0] as f32, joint.position[1] as f32),
|
||||
joint.radius as f32,
|
||||
egui::Color32::RED,
|
||||
color,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -88,13 +123,14 @@ impl Displayer {
|
|||
viewport: &Viewport,
|
||||
segment: &Segment,
|
||||
endpoints: [[i64; 2]; 2],
|
||||
color: egui::Color32,
|
||||
) {
|
||||
ui.painter().line_segment(
|
||||
[
|
||||
egui::pos2(endpoints[0][0] as f32, endpoints[0][1] as f32),
|
||||
egui::pos2(endpoints[1][0] as f32, endpoints[1][1] as f32),
|
||||
],
|
||||
egui::Stroke::new(segment.half_width as f32 * 2.0, egui::Color32::RED),
|
||||
egui::Stroke::new(segment.half_width as f32 * 2.0, color),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +140,7 @@ impl Displayer {
|
|||
ui: &egui::Ui,
|
||||
viewport: &Viewport,
|
||||
polygon: &Polygon,
|
||||
color: egui::Color32,
|
||||
) {
|
||||
let points: Vec<egui::Pos2> = polygon
|
||||
.vertices
|
||||
|
|
@ -114,7 +151,7 @@ impl Displayer {
|
|||
ui.painter().add(egui::Shape::convex_polygon(
|
||||
points,
|
||||
egui::Color32::RED,
|
||||
egui::Stroke::new(5.0 / viewport.scale_factor(), egui::Color32::RED),
|
||||
egui::Stroke::new(5.0 / viewport.scale_factor(), color),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
mod action;
|
||||
mod actions;
|
||||
mod app;
|
||||
mod appearance_panel;
|
||||
mod displayer;
|
||||
mod menu_bar;
|
||||
mod translator;
|
||||
|
|
|
|||
|
|
@ -4,14 +4,24 @@
|
|||
|
||||
use topola::Board;
|
||||
|
||||
use crate::translator::Translator;
|
||||
use crate::{appearance_panel::AppearancePanel, translator::Translator};
|
||||
|
||||
pub struct Workspace {
|
||||
pub board: Board,
|
||||
pub appearance_panel: AppearancePanel,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(board: Board, tr: &Translator) -> Self {
|
||||
Self { board }
|
||||
let appearance_panel = AppearancePanel::new(&board);
|
||||
|
||||
Self {
|
||||
board,
|
||||
appearance_panel,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
||||
self.appearance_panel.update(ctx, &self.board);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ pub struct Board {
|
|||
}
|
||||
|
||||
impl Board {
|
||||
pub fn new(boundary: Vec<[i64; 2]>) -> Self {
|
||||
pub fn new(boundary: Vec<[i64; 2]>, layer_count: usize) -> Self {
|
||||
Self {
|
||||
layout: Layout::new(boundary),
|
||||
layout: Layout::new(boundary, layer_count),
|
||||
layer_names: BiBTreeMap::new(),
|
||||
net_names: BiBTreeMap::new(),
|
||||
}
|
||||
|
|
@ -36,11 +36,12 @@ impl Board {
|
|||
|
||||
pub fn with_names(
|
||||
boundary: Vec<[i64; 2]>,
|
||||
layer_count: usize,
|
||||
layer_names: BiBTreeMap<usize, String>,
|
||||
net_names: BiBTreeMap<usize, String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
layout: Layout::new(boundary),
|
||||
layout: Layout::new(boundary, layer_count),
|
||||
layer_names,
|
||||
net_names,
|
||||
}
|
||||
|
|
@ -66,20 +67,20 @@ impl Board {
|
|||
self.layout.add_polygon(polygon)
|
||||
}
|
||||
|
||||
pub fn layer_name(&self, layer: usize) -> &str {
|
||||
&self.layer_names.get_by_left(&layer).unwrap()
|
||||
pub fn layer_name(&self, layer: usize) -> Option<&str> {
|
||||
self.layer_names.get_by_left(&layer).map(String::as_str)
|
||||
}
|
||||
|
||||
pub fn layer_id(&self, name: &str) -> usize {
|
||||
*self.layer_names.get_by_right(name).unwrap()
|
||||
pub fn layer_id(&self, name: &str) -> Option<usize> {
|
||||
self.layer_names.get_by_right(name).copied()
|
||||
}
|
||||
|
||||
pub fn net_name(&self, net: usize) -> &str {
|
||||
&self.net_names.get_by_left(&net).unwrap()
|
||||
pub fn net_name(&self, net: usize) -> Option<&str> {
|
||||
self.net_names.get_by_left(&net).map(String::as_str)
|
||||
}
|
||||
|
||||
pub fn net_id(&self, name: &str) -> usize {
|
||||
*self.net_names.get_by_right(name).unwrap()
|
||||
pub fn net_id(&self, name: &str) -> Option<usize> {
|
||||
self.net_names.get_by_right(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ pub struct Polygon {
|
|||
pub struct Layout {
|
||||
boundary: Vec<[i64; 2]>,
|
||||
place_boundary: Vec<[i64; 2]>,
|
||||
layer_count: usize,
|
||||
|
||||
joints: Recorder<StableVec<Joint>>,
|
||||
segments: Recorder<StableVec<Segment>>,
|
||||
arcs: Recorder<StableVec<Arc>>,
|
||||
|
|
@ -140,10 +142,12 @@ pub struct Layout {
|
|||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn new(boundary: Vec<[i64; 2]>) -> Self {
|
||||
pub fn new(boundary: Vec<[i64; 2]>, layer_count: usize) -> Self {
|
||||
Self {
|
||||
boundary: boundary.clone(),
|
||||
place_boundary: boundary,
|
||||
layer_count,
|
||||
|
||||
joints: Recorder::new(StableVec::new()),
|
||||
segments: Recorder::new(StableVec::new()),
|
||||
arcs: Recorder::new(StableVec::new()),
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ impl Board {
|
|||
.into_iter()
|
||||
.map(|p| [p.x as i64, p.y as i64])
|
||||
.collect(),
|
||||
dsn.pcb.structure.layers.len(),
|
||||
layer_names,
|
||||
net_names,
|
||||
);
|
||||
|
|
@ -251,7 +252,7 @@ impl Board {
|
|||
}
|
||||
|
||||
fn layer(board: &Board, layers: &[Layer], name: &str, front: bool) -> usize {
|
||||
let image_layer = board.layer_id(name);
|
||||
let image_layer = board.layer_id(name).unwrap();
|
||||
|
||||
if front {
|
||||
image_layer
|
||||
|
|
|
|||
Loading…
Reference in New Issue