continued ui improvement
This commit is contained in:
parent
d53e294b7c
commit
b73467468a
16 changed files with 1551 additions and 994 deletions
|
@ -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
1261
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"] }
|
||||
|
|
6
crates/client/dist/index.html
vendored
6
crates/client/dist/index.html
vendored
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
@ -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")
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
31
crates/client/src/ui/shell/bars.rs
Normal file
31
crates/client/src/ui/shell/bars.rs
Normal 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();
|
||||
}
|
|
@ -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(®istered_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, ®istered_layer.location)).weak());
|
||||
ui.label(
|
||||
RichText::new(format!(
|
||||
"{} {}",
|
||||
egui_phosphor::regular::NAVIGATION_ARROW,
|
||||
®istered_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, ®istered_layer.source)).weak());
|
||||
ui.label(
|
||||
RichText::new(format!(
|
||||
"{} {}",
|
||||
egui_phosphor::regular::IDENTIFICATION_BADGE,
|
||||
®istered_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
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
*/
|
|
@ -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;
|
31
crates/client/src/ui/shell/right_bar.rs
Normal file
31
crates/client/src/ui/shell/right_bar.rs
Normal 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()
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
}
|
Loading…
Add table
Reference in a new issue