continued ui improvement

This commit is contained in:
core 2025-03-20 22:56:16 -05:00
parent d53e294b7c
commit b73467468a
16 changed files with 1551 additions and 994 deletions

View file

@ -1,3 +1,6 @@
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-use-ld="]
rustflags = ["-C", "link-arg=-use-ld="]
[target.wasm32-unknown-unknown]
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']

1261
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,8 @@ ehttp = "0.5"
image = "0.25"
bytemuck = "1.22"
nalgebra-glm = "0.19"
rand = "0.9"
getrandom = { version = "0.3", features = ["wasm_js"] }
serde = { version = "1", features = ["derive"] }
toml = "0.8"
egui_extras = { version = "0.31", features = ["serde"] }

View file

@ -94,7 +94,7 @@
}
}
</style>
<link rel="modulepreload" href="/wxbox-client-96cce58fa4aaba7a.js" crossorigin="anonymous" integrity="sha384-rOwE8Z+dDRuQLsg/fUPw7V9/GJ3Oifopq6b88/pPM7xq2QmCD54SJdnx/SgS1/R1"><link rel="preload" href="/wxbox-client-96cce58fa4aaba7a_bg.wasm" crossorigin="anonymous" integrity="sha384-Rw805YQqcRKZ9iiDhDqyezvRhVu5nQt+IPPnlZCFQ+bPI7pIaxw5QmFJ4EuhBRVf" as="fetch" type="application/wasm"></head>
<link rel="modulepreload" href="/wxbox-client-d12479fc5b6a11f2.js" crossorigin="anonymous" integrity="sha384-aCfm9OVd/TH2L9v1Uxwdt7G/eHUDCXYq8UquzFFkRjWK794kAfYG4RWiDDu/i5DW"><link rel="preload" href="/wxbox-client-d12479fc5b6a11f2_bg.wasm" crossorigin="anonymous" integrity="sha384-tXCe7d8yqYuqyhU/Tu9i3EBZGCugKf3HuhaPOamXrdvv7vV2oawP1qwlsWmrEL68" as="fetch" type="application/wasm"></head>
<body>
<!-- The WASM code will resize the canvas dynamically -->
@ -111,8 +111,8 @@
<script type="module">
import init, * as bindings from '/wxbox-client-96cce58fa4aaba7a.js';
const wasm = await init({ module_or_path: '/wxbox-client-96cce58fa4aaba7a_bg.wasm' });
import init, * as bindings from '/wxbox-client-d12479fc5b6a11f2.js';
const wasm = await init({ module_or_path: '/wxbox-client-d12479fc5b6a11f2_bg.wasm' });
window.wasmBindings = bindings;

View file

@ -7,20 +7,20 @@ use serde::{Deserialize, Serialize};
use tracing::debug;
use crate::map::{Map};
use crate::map::sources::baselayers::BASELAYER_OSM;
use crate::map::tiles::LayerManager;
use crate::ui::layer_selector::{LayerSelector};
use crate::map::tiles::{ActiveLayer, LayerId, LayerManager};
use crate::ui::shell::footer::footer;
use crate::ui::shell::left_bar::left_bar;
use crate::ui::shell::right_bar::right_bar;
use crate::ui::shell::top_bar::top_bar;
use crate::ui::tokens::{DESIGN_TOKENS, DesignTokens};
#[derive(Serialize, Deserialize)]
pub struct App {
map: Map,
layer_selector: LayerSelector,
widget_gallery: WidgetGallery,
pub(crate) frame_time_history: History<f32>,
add_layer_open: bool
add_layer_open: bool,
selected_layer: Option<LayerId>,
}
impl App {
@ -42,15 +42,19 @@ impl App {
let mut map = Map::new(cc).unwrap();
let mut layer_manager = LayerManager::default();
layer_manager.register_builtin_layers();
layer_manager.active_layers.push(BASELAYER_OSM.source_id);
layer_manager.active_layers.push(ActiveLayer {
source_id: BASELAYER_OSM.source_id,
layer_id: LayerId::new(),
visible: true,
});
map.layer_manager = layer_manager;
Self {
map,
layer_selector: LayerSelector::default(),
widget_gallery: WidgetGallery::default(),
frame_time_history: History::new(1..100, 0.5),
add_layer_open: false
add_layer_open: false,
selected_layer: None
}
}
}
@ -67,7 +71,8 @@ impl eframe::App for App {
top_bar(ctx);
footer(ctx, &self);
left_bar(ctx, &mut self.add_layer_open, &mut self.map.layer_manager);
left_bar(ctx, &mut self.add_layer_open, &mut self.map.layer_manager, &mut self.selected_layer);
right_bar(ctx);
/*
egui::SidePanel::left("sidebar")

View file

@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
use tracing::debug;
use crate::map::render::{ExtraRenderOptions, MapRender};
use crate::map::sources::baselayers::BASELAYER_OSM;
use crate::map::tiles::{LayerId, LayerManager, LayerSource, Tile, XCoord, YCoord, ZoomLevel};
use crate::map::tiles::{SourceId, LayerManager, LayerSource, Tile, XCoord, YCoord, ZoomLevel};
#[derive(Serialize, Deserialize)]
#[serde(default)]
@ -212,7 +212,7 @@ impl Map {
// layers are stored top-down, but we want to render tiles bottom-up
for active_layer in active_layers.iter().rev() {
let layer_info_opt = {
self.layer_manager.registered_layers.get(active_layer).unwrap().clone()
self.layer_manager.registered_sources.get(&active_layer.source_id).unwrap().clone()
};
let x_buffer_tiles = 2;
@ -236,7 +236,7 @@ impl Map {
let tile = self.load_tile(ctx, z_clamped as ZoomLevel, tx, ty, &layer_info_opt);
tiles.push(TilesetTile {
tileid: (z_clamped as usize, tx, ty, *active_layer),
tileid: (z_clamped as usize, tx, ty, active_layer.source_id),
x: (256.0*ox as f32 - x_tile_frac as f32),
y: (-256.0*oy as f32 + y_tile_frac as f32),
tile: match tile.promise.ready() {
@ -288,11 +288,11 @@ impl Map {
pub struct Tileset {
pub tiles: Arc<Mutex<Vec<TilesetTile>>>,
zoom: f32,
retained_tiles: Vec<(ZoomLevel, XCoord, YCoord, LayerId)>
retained_tiles: Vec<(ZoomLevel, XCoord, YCoord, SourceId)>
}
#[derive(Clone, Debug)]
struct TilesetTile {
tileid: (ZoomLevel, XCoord, YCoord, LayerId),
tileid: (ZoomLevel, XCoord, YCoord, SourceId),
x: f32,
y: f32,
tile: Option<DynamicImage>,

View file

@ -4,7 +4,7 @@ use std::mem;
use glow::{Buffer, PixelUnpackData, Program, Texture, VertexArray};
use serde::{Deserialize, Serialize};
use tracing::{error, warn};
use crate::map::tiles::{LayerId, LayerManager, XCoord, YCoord, ZoomLevel};
use crate::map::tiles::{SourceId, LayerManager, XCoord, YCoord, ZoomLevel};
use crate::map::Tileset;
pub struct MapRender {
@ -12,7 +12,7 @@ pub struct MapRender {
vao: VertexArray,
ebo: Buffer,
shader_program: Program,
texture_cache: HashMap<(ZoomLevel, XCoord, YCoord, LayerId), Texture>
texture_cache: HashMap<(ZoomLevel, XCoord, YCoord, SourceId), Texture>
}
#[allow(unsafe_code)]

View file

@ -1,3 +1,4 @@
use std::cmp::PartialEq;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use image::DynamicImage;
@ -11,24 +12,25 @@ use crate::map::Tileset;
pub type ZoomLevel = usize;
pub type XCoord = usize;
pub type YCoord = usize;
pub type LayerId = u64;
pub type SourceId = u64;
#[derive(Default, Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct LayerManager {
pub registered_layers: HashMap<LayerId, LayerSource>,
pub active_layers: Vec<LayerId>,
pub registered_sources: HashMap<SourceId, LayerSource>,
pub active_layers: Vec<ActiveLayer>,
#[serde(skip)]
pub tiles: HashMap<(ZoomLevel, XCoord, YCoord, LayerId), (Tile, u64)>,
pub tiles: HashMap<(ZoomLevel, XCoord, YCoord, SourceId), (Tile, u64)>,
pub tile_cache_retention: u64
}
impl LayerManager {
pub fn register_layer(&mut self, layer: &LayerSource) {
self.registered_layers.insert(layer.source_id, layer.clone());
self.registered_sources.insert(layer.source_id, layer.clone());
}
pub fn unregister_layer(&mut self, layer_id: &LayerId) {
self.registered_layers.remove(layer_id);
self.active_layers.retain(|u| u != layer_id);
pub fn unregister_layer(&mut self, layer_id: &SourceId) {
self.registered_sources.remove(layer_id);
self.active_layers.retain(|u| u.source_id != *layer_id);
}
pub fn register_builtin_layers(&mut self) {
let layers = [
@ -53,7 +55,7 @@ impl LayerManager {
pub fn remove_unregistered_layers(&mut self) {
// remove any
self.active_layers.retain(|u| self.registered_layers.contains_key(u));
self.active_layers.retain(|u| self.registered_sources.contains_key(&u.source_id));
}
pub fn garbage_collect(&mut self, current_tileset: &mut Tileset) {
let tiles_onscreen = current_tileset.tiles.lock().unwrap();
@ -103,4 +105,29 @@ impl Debug for Tile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", if self.promise.ready().is_some() { "<loaded texture>" } else { "<texture promise>" })
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ActiveLayer {
pub source_id: SourceId,
pub layer_id: LayerId,
pub visible: bool
}
#[derive(Hash, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayerId(u32);
impl LayerId {
pub fn new() -> Self {
Self(rand::random())
}
}
impl Debug for LayerId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
impl From<LayerId> for egui::Id {
fn from(id: LayerId) -> Self {
Self::new(id)
}
}

View file

@ -1,98 +0,0 @@
use std::collections::HashMap;
use egui::{Button, Color32, Frame, Id, Ui, vec2};
use serde::{Deserialize, Serialize};
use crate::map::tiles::{LayerId, LayerManager, LayerSource};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Location {
row: usize,
}
fn active_layer_reorderer(ui: &mut Ui, layers: &mut Vec<LayerId>, layer_registry: &HashMap<LayerId, LayerSource>) {
let mut from = None;
let mut to = None;
let frame = Frame::default().inner_margin(4.0);
let (_, dropped_payload) = ui.dnd_drop_zone::<Location, ()>(frame, |ui| {
ui.set_min_size(vec2(64.0, 100.0));
for (row_idx, item) in layers.iter().enumerate() {
let item_id = Id::new(("layerselect_dnd", row_idx));
let item_location = Location {
row: row_idx
};
let response = ui.dnd_drag_source(item_id, item_location, |ui| {
let opt = layer_registry.get(item);
let layer = opt.as_ref().unwrap();
ui.label(layer.display_name.clone());
}).response;
if let (Some(pointer), Some(hovered_payload)) = (
ui.input(|i| i.pointer.interact_pos()),
response.dnd_hover_payload::<Location>(),
) {
let rect = response.rect;
let stroke = egui::Stroke::new(1.0, Color32::WHITE);
let insert_row_idx = if *hovered_payload == item_location {
ui.painter().hline(rect.x_range(), rect.center().y, stroke);
row_idx
} else if pointer.y < rect.center().y {
ui.painter().hline(rect.x_range(), rect.top(), stroke);
row_idx
} else {
ui.painter().hline(rect.x_range(), rect.bottom(), stroke);
row_idx + 1
};
if let Some(dragged_payload) = response.dnd_release_payload() {
from = Some(dragged_payload);
to = Some(Location {
row: insert_row_idx
});
}
}
}
});
if let Some(dragged_payload) = dropped_payload {
from = Some(dragged_payload);
to = Some(Location {
row: usize::MAX
});
}
if let (Some(from), Some(mut to)) = (from, to) {
to.row -= (from.row < to.row) as usize;
let item = layers.remove(from.row);
to.row = to.row.min(layers.len());
layers.insert(to.row, item);
}
}
#[derive(Serialize, Deserialize, Default)]
pub struct LayerSelector {
add_layer_layerid: LayerId
}
impl LayerSelector {
pub fn ui(&mut self, ui: &mut Ui, layer_manager: &mut LayerManager) {
active_layer_reorderer(ui, &mut layer_manager.active_layers, &layer_manager.registered_layers);
egui::ComboBox::from_id_salt("layerselect")
.selected_text({
if self.add_layer_layerid == 0 { "Select a layer".to_string() }
else {
let opt = layer_manager.registered_layers.get(&self.add_layer_layerid);
opt.as_ref().unwrap().display_name.clone()
}
})
.show_ui(ui, |ui| {
for (layerid, layer) in &layer_manager.registered_layers {
ui.selectable_value(&mut self.add_layer_layerid, *layerid, layer.display_name.clone());
}
});
if ui.add_enabled(self.add_layer_layerid != 0, Button::new("Add")).clicked() {
layer_manager.active_layers.push(self.add_layer_layerid);
}
}
}

View file

@ -2,7 +2,6 @@ use std::any::Any;
use egui::emath::GuiRounding;
use egui::{emath, CursorIcon, Id, InnerResponse, LayerId, Order, Sense, Ui, UiBuilder};
pub mod layer_selector;
pub mod tokens;
pub mod shell;

View file

@ -0,0 +1,31 @@
use egui::{Align, FontFamily, Frame, Layout, RichText, Ui};
use crate::ui::UiExt;
pub fn pane_header(
ui: &mut Ui,
title: &str,
icon: Option<&str>,
top_separator: bool,
add_contents: impl FnOnce(&mut Ui),
) {
if top_separator {
ui.full_span_separator();
}
let mut frame = Frame::new();
frame.inner_margin.left = 8;
frame.inner_margin.right = 8;
frame.show(ui, |ui| {
ui.horizontal(|ui| {
if let Some(icon) = icon {
ui.label(RichText::new(icon).family(FontFamily::Name("icon-only".into())).strong());
}
ui.label(RichText::new(title).family(FontFamily::Name("semibold".into())).strong());
ui.with_layout(Layout::right_to_left(Align::Center), add_contents);
});
});
ui.full_span_separator();
}

View file

@ -1,29 +1,21 @@
use eframe::emath::Align;
use egui::{Color32, CursorIcon, FontFamily, Frame, Id, Layout, Response, RichText, ScrollArea, Theme, Ui, vec2, Visuals, CornerRadius, Button, Margin, Label, Rect, Sense};
use egui::epaint::Marginf;
use egui::frame::Prepared;
use tracing::debug;
use crate::map::tiles::{LayerManager, LayerTypeHint};
use std::collections::HashMap;
use crate::map::tiles::{ActiveLayer, LayerId, LayerManager, LayerSource, LayerTypeHint, SourceId};
use crate::ui::tokens::DESIGN_TOKENS;
use crate::ui::UiExt;
use eframe::emath::Align;
use egui::epaint::Marginf;
use egui::frame::Prepared;
use egui::{vec2, Button, Color32, CornerRadius, CursorIcon, FontFamily, Frame, Id, Label, Layout, Margin, Rect, Response, RichText, ScrollArea, Sense, Separator, Theme, Ui, UiBuilder, Visuals};
use tracing::debug;
use crate::ui::shell::bars::pane_header;
fn outer_rect(f: &Prepared) -> Rect {
let content_rect = f.content_ui.min_rect();
content_rect
+ f.frame.inner_margin
+ Marginf::from(f.frame.stroke.width)
+ f.frame.outer_margin
content_rect + f.frame.inner_margin + Marginf::from(f.frame.stroke.width) + f.frame.outer_margin
}
pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut LayerManager) {
pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut LayerManager, selected_layer: &mut Option<LayerId>) {
let mut frame = Frame::side_top_panel(&ctx.style());
frame.inner_margin.top = 4;
frame.inner_margin.left = 0;
@ -32,24 +24,52 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
egui::SidePanel::left("left_panel")
.frame(frame)
.show(ctx, |ui| {
pane_header(ui, "Layers", Some(egui_phosphor::regular::STACK), false, |ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
if ui.button(RichText::new(egui_phosphor::regular::PLUS).size(12.0)).clicked() {
*add_open = true;
}
});
for (idx, layer) in layer_manager.active_layers.iter().enumerate() {
ui.dnd_drag_source(
Id::new(idx as u64 + layer),
idx,
|ui| {
if ui.button("hello").clicked() {
*add_open = true;
}
pane_header(
ui,
"Layers",
Some(egui_phosphor::regular::STACK),
false,
|ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
if ui
.button(RichText::new(egui_phosphor::regular::PLUS).size(12.0))
.clicked()
{
*add_open = true;
}
},
);
ui.label(format!("{:?}", selected_layer));
let lm_registry = &layer_manager.registered_sources;
let layers_to_draw = layer_manager.active_layers.len();
let mut layers_to_remove = vec![];
for (idx, layer) in layer_manager.active_layers.iter_mut().enumerate() {
let retain = draw_layer(
ui,
layer,
lm_registry,
selected_layer
);
if !retain {
layers_to_remove.push(layer.layer_id);
}
/*
if idx != layers_to_draw - 1 {
ui.add(
Separator::default()
.shrink(72.0)
);
}*/ // TODO @tm85 this looks weird, please mess with it at your earliest convenience
}
layer_manager.active_layers.retain(|layer| !layers_to_remove.contains(&layer.layer_id));
});
if *add_open {
@ -59,14 +79,23 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
egui::Modal::new(Id::new("add_source_modal"))
.frame(frame)
.show(ctx, |ui| {
pane_header(ui, "Add layer", Some(egui_phosphor::regular::STACK_PLUS), false, |ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
if ui.button(RichText::new(egui_phosphor::regular::X).size(12.0)).clicked() {
*add_open = false;
}
});
pane_header(
ui,
"Add layer",
Some(egui_phosphor::regular::STACK_PLUS),
false,
|ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
if ui
.button(RichText::new(egui_phosphor::regular::X).size(12.0))
.clicked()
{
*add_open = false;
}
},
);
for (id, registered_layer) in &layer_manager.registered_layers {
for (id, registered_layer) in &layer_manager.registered_sources {
// ghost button
let mut frame = Frame::new();
@ -80,8 +109,18 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
{
frame.content_ui.horizontal(|ui| {
let mut frame = Frame::new()
.fill(DESIGN_TOKENS.get_color("colors.base.4", if ui.style().visuals.dark_mode { Theme::Dark } else { Theme::Light }).unwrap());
let mut frame = Frame::new().fill(
DESIGN_TOKENS
.get_color(
"colors.base.4",
if ui.style().visuals.dark_mode {
Theme::Dark
} else {
Theme::Light
},
)
.unwrap(),
);
frame.inner_margin.top = 4;
frame.inner_margin.left = 8;
@ -91,26 +130,26 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
frame.corner_radius = CornerRadius::from(4);
frame.show(ui, |ui| {
let icon = match registered_layer.type_hint {
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET,
};
let icon = match registered_layer.type_hint {
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET
};
ui.label(RichText::new(icon).size(24.0));
});
ui.label(RichText::new(icon).size(24.0));
});
ui.vertical(|ui| {
ui.label(&registered_layer.display_name);
ui.horizontal(|ui| {
let type_hint = match registered_layer.type_hint {
LayerTypeHint::Baselayer => "Baselayer",
LayerTypeHint::RadarData => "Radar data",
};
let icon = match registered_layer.type_hint {
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET
LayerTypeHint::Baselayer => {
egui_phosphor::regular::MAP_TRIFOLD
}
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET,
};
let type_lbl = format!("{icon} {type_hint}");
@ -120,26 +159,39 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
|ui| {
ui.label(RichText::new(type_lbl).weak());
ui.add_space(ui.available_width());
}
},
);
ui.allocate_ui_with_layout(
vec2(120.0, 20.0),
Layout::left_to_right(Align::Center),
|ui| {
ui.label(RichText::new(format!("{} {}", egui_phosphor::regular::NAVIGATION_ARROW, &registered_layer.location)).weak());
ui.label(
RichText::new(format!(
"{} {}",
egui_phosphor::regular::NAVIGATION_ARROW,
&registered_layer.location
))
.weak(),
);
ui.add_space(ui.available_width());
}
},
);
ui.allocate_ui_with_layout(
vec2(100.0, 20.0),
Layout::left_to_right(Align::Center),
|ui| {
ui.label(RichText::new(format!("{} {}", egui_phosphor::regular::IDENTIFICATION_BADGE, &registered_layer.source)).weak());
ui.label(
RichText::new(format!(
"{} {}",
egui_phosphor::regular::IDENTIFICATION_BADGE,
&registered_layer.source
))
.weak(),
);
ui.add_space(ui.available_width());
}
},
);
});
});
@ -149,102 +201,156 @@ pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut La
}
let response = ui.allocate_rect(outer_rect(&frame), Sense::click_and_drag());
if response.hovered() {
frame.frame.fill = DESIGN_TOKENS.get_color("colors.base.7", if ui.style().visuals.dark_mode { Theme::Dark } else { Theme::Light }).unwrap();
ui.ctx().output_mut(|u| u.cursor_icon = CursorIcon::PointingHand);
frame.frame.fill = DESIGN_TOKENS
.get_color(
"colors.base.7",
if ui.style().visuals.dark_mode {
Theme::Dark
} else {
Theme::Light
},
)
.unwrap();
ui.ctx()
.output_mut(|u| u.cursor_icon = CursorIcon::PointingHand);
}
if response.clicked() {
layer_manager.active_layers.push(*id);
layer_manager.active_layers.push(ActiveLayer {
source_id: *id,
layer_id: LayerId::new(),
visible: true,
});
debug!("{:?}", layer_manager);
*add_open = false;
}
frame.paint(ui);
}
});
}
}
fn pane_header(ui: &mut Ui, title: &str, icon: Option<&str>, top_separator: bool, add_contents: impl FnOnce(&mut Ui)) {
if top_separator {
ui.full_span_separator();
}
let mut frame = Frame::new();
frame.inner_margin.left = 8;
frame.inner_margin.right = 8;
frame.show(ui, |ui| {
ui.horizontal(|ui| {
let text = if let Some(icon) = icon {
RichText::new(format!("{} {}", icon, title))
} else {
RichText::new(title)
fn draw_layer(ui: &mut Ui, layer: &mut ActiveLayer, registry: &HashMap<SourceId, LayerSource>, selected_layer: &mut Option<LayerId>) -> bool {
let mut retain = true;
ui.scope_builder(
UiBuilder::new()
.id_salt(layer.layer_id)
.sense(Sense::click_and_drag()),
|ui| {
let source = registry
.get(&layer.source_id)
.unwrap();
let title = source.display_name.as_str();
let icon = match source.type_hint {
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET,
};
ui.label(
text
.family(FontFamily::Name("semibold".into()))
.strong()
);
ui.with_layout(Layout::right_to_left(Align::Center), add_contents);
});
});
let mut any_children_hovered = false;
ui.full_span_separator();
}
let mut frame = Frame::new();
//frame.fill = ui.style().visuals.hyperlink_color;
frame.inner_margin.left = 8;
frame.inner_margin.right = 8;
frame.inner_margin.top = 2;
frame.inner_margin.bottom = 2;
/*
fn layer(ui: &mut Ui, title: &str, icon: &str) {
ui.passthrough_dnd_drag_source(Id::new(title), title.to_owned(), |ui| {
let mut frame = Frame::new();
//frame.fill = ui.style().visuals.hyperlink_color;
frame.inner_margin.left = 8;
frame.inner_margin.right = 8;
frame.inner_margin.top = 2;
frame.inner_margin.bottom = 2;
let mut frame = frame.begin(ui);
{
frame.content_ui.horizontal(|ui| {
ui.label(
RichText::new(egui_phosphor::regular::LIST)
.size(12.0)
.family(FontFamily::Name("icon-only".into()))
.weak()
);
ui.add_space(8.0);
ui.label(RichText::new(
format!("{icon} {title}")
));
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
ui.button(
RichText::new(egui_phosphor::regular::EYE)
.size(12.0)
.family(FontFamily::Name("icon-only".into()))
let mut frame = frame.begin(ui);
{
frame.content_ui.horizontal(|ui| {
ui.add(
Label::new(
RichText::new(egui_phosphor::regular::DOTS_SIX_VERTICAL)
.size(12.0)
.family(FontFamily::Name("icon-only".into()))
.weak(),
)
.sense(Sense::empty())
.selectable(false)
);
ui.button(
RichText::new(egui_phosphor::regular::TRASH)
ui.add_space(8.0);
let mut name_label = RichText::new(format!("{icon} {title} ({:?})", layer.layer_id));
if !layer.visible {
name_label = name_label.weak();
}
ui.add(Label::new(name_label).sense(Sense::empty()).selectable(false));
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
let mut visibility_label = RichText::new(if layer.visible {
egui_phosphor::regular::EYE
} else {
egui_phosphor::regular::EYE_SLASH
})
.size(12.0)
.family(FontFamily::Name("icon-only".into()))
);
.family(FontFamily::Name("icon-only".into()));
if !layer.visible {
visibility_label = visibility_label.weak();
}
let visibility_btn = ui.button(
visibility_label
);
any_children_hovered = any_children_hovered || visibility_btn.hovered();
if visibility_btn.clicked() {
layer.visible = !layer.visible;
}
let mut delete_label = RichText::new(egui_phosphor::regular::TRASH)
.size(12.0)
.family(FontFamily::Name("icon-only".into()));
if !layer.visible {
delete_label = delete_label.weak();
}
let delete_button = ui.button(
delete_label
);
any_children_hovered = any_children_hovered || delete_button.hovered();
if delete_button.clicked() {
retain = false;
}
});
});
});
}
let response = frame.allocate_space(ui);
if response.hovered() {
frame.frame.fill = ;
ui.ctx().output_mut(|u| u.cursor_icon = CursorIcon::Grab);
}
frame.paint(ui);
}
frame.allocate_space(ui);
let response = ui.response();
if response.hovered() && !any_children_hovered {
frame.frame.fill = ui.visuals().widgets.active.weak_bg_fill;
ui.ctx().output_mut(|u| u.cursor_icon = CursorIcon::Grab);
}
if response.clicked() {
*selected_layer = Some(layer.layer_id);
}
if let Some(sel_layer) = selected_layer {
if *sel_layer == layer.layer_id {
if !layer.visible {
frame.frame.fill = ui.visuals().widgets.inactive.bg_fill;
} else {
frame.frame.fill = ui.visuals().widgets.hovered.bg_fill;
}
}
}
frame.paint(ui);
}
);
retain
});
}
*/

View file

@ -1,3 +1,5 @@
pub mod top_bar;
pub mod footer;
pub mod left_bar;
pub mod left_bar;
pub mod bars;
pub mod right_bar;

View file

@ -0,0 +1,31 @@
use egui::{Align, Direction, Frame, Layout, RichText};
use crate::ui::shell::bars::pane_header;
pub fn right_bar(ctx: &egui::Context) {
let mut frame = Frame::side_top_panel(&ctx.style());
frame.inner_margin.top = 4;
frame.inner_margin.left = 0;
frame.inner_margin.right = 0;
egui::SidePanel::right("right_panel")
.frame(frame)
.show(ctx, |ui| {
pane_header(
ui,
"Properties",
Some(egui_phosphor::regular::INFO),
false,
|ui| {}
);
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
ui.label(
RichText::new("Select a layer in the Layers panel to modify it's properties").italics()
);
}
);
});
}