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]
|
[target.x86_64-unknown-linux-gnu]
|
||||||
linker = "clang"
|
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"
|
image = "0.25"
|
||||||
bytemuck = "1.22"
|
bytemuck = "1.22"
|
||||||
nalgebra-glm = "0.19"
|
nalgebra-glm = "0.19"
|
||||||
|
rand = "0.9"
|
||||||
|
getrandom = { version = "0.3", features = ["wasm_js"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
egui_extras = { version = "0.31", features = ["serde"] }
|
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>
|
</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>
|
<body>
|
||||||
<!-- The WASM code will resize the canvas dynamically -->
|
<!-- The WASM code will resize the canvas dynamically -->
|
||||||
|
@ -111,8 +111,8 @@
|
||||||
|
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import init, * as bindings from '/wxbox-client-96cce58fa4aaba7a.js';
|
import init, * as bindings from '/wxbox-client-d12479fc5b6a11f2.js';
|
||||||
const wasm = await init({ module_or_path: '/wxbox-client-96cce58fa4aaba7a_bg.wasm' });
|
const wasm = await init({ module_or_path: '/wxbox-client-d12479fc5b6a11f2_bg.wasm' });
|
||||||
|
|
||||||
|
|
||||||
window.wasmBindings = bindings;
|
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 tracing::debug;
|
||||||
use crate::map::{Map};
|
use crate::map::{Map};
|
||||||
use crate::map::sources::baselayers::BASELAYER_OSM;
|
use crate::map::sources::baselayers::BASELAYER_OSM;
|
||||||
use crate::map::tiles::LayerManager;
|
use crate::map::tiles::{ActiveLayer, LayerId, LayerManager};
|
||||||
use crate::ui::layer_selector::{LayerSelector};
|
|
||||||
use crate::ui::shell::footer::footer;
|
use crate::ui::shell::footer::footer;
|
||||||
use crate::ui::shell::left_bar::left_bar;
|
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::shell::top_bar::top_bar;
|
||||||
use crate::ui::tokens::{DESIGN_TOKENS, DesignTokens};
|
use crate::ui::tokens::{DESIGN_TOKENS, DesignTokens};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
map: Map,
|
map: Map,
|
||||||
layer_selector: LayerSelector,
|
|
||||||
widget_gallery: WidgetGallery,
|
widget_gallery: WidgetGallery,
|
||||||
pub(crate) frame_time_history: History<f32>,
|
pub(crate) frame_time_history: History<f32>,
|
||||||
add_layer_open: bool
|
add_layer_open: bool,
|
||||||
|
selected_layer: Option<LayerId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -42,15 +42,19 @@ impl App {
|
||||||
let mut map = Map::new(cc).unwrap();
|
let mut map = Map::new(cc).unwrap();
|
||||||
let mut layer_manager = LayerManager::default();
|
let mut layer_manager = LayerManager::default();
|
||||||
layer_manager.register_builtin_layers();
|
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;
|
map.layer_manager = layer_manager;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
map,
|
map,
|
||||||
layer_selector: LayerSelector::default(),
|
|
||||||
widget_gallery: WidgetGallery::default(),
|
widget_gallery: WidgetGallery::default(),
|
||||||
frame_time_history: History::new(1..100, 0.5),
|
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);
|
top_bar(ctx);
|
||||||
footer(ctx, &self);
|
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")
|
egui::SidePanel::left("sidebar")
|
||||||
|
|
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use crate::map::render::{ExtraRenderOptions, MapRender};
|
use crate::map::render::{ExtraRenderOptions, MapRender};
|
||||||
use crate::map::sources::baselayers::BASELAYER_OSM;
|
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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -212,7 +212,7 @@ impl Map {
|
||||||
// layers are stored top-down, but we want to render tiles bottom-up
|
// layers are stored top-down, but we want to render tiles bottom-up
|
||||||
for active_layer in active_layers.iter().rev() {
|
for active_layer in active_layers.iter().rev() {
|
||||||
let layer_info_opt = {
|
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;
|
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);
|
let tile = self.load_tile(ctx, z_clamped as ZoomLevel, tx, ty, &layer_info_opt);
|
||||||
tiles.push(TilesetTile {
|
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),
|
x: (256.0*ox as f32 - x_tile_frac as f32),
|
||||||
y: (-256.0*oy as f32 + y_tile_frac as f32),
|
y: (-256.0*oy as f32 + y_tile_frac as f32),
|
||||||
tile: match tile.promise.ready() {
|
tile: match tile.promise.ready() {
|
||||||
|
@ -288,11 +288,11 @@ impl Map {
|
||||||
pub struct Tileset {
|
pub struct Tileset {
|
||||||
pub tiles: Arc<Mutex<Vec<TilesetTile>>>,
|
pub tiles: Arc<Mutex<Vec<TilesetTile>>>,
|
||||||
zoom: f32,
|
zoom: f32,
|
||||||
retained_tiles: Vec<(ZoomLevel, XCoord, YCoord, LayerId)>
|
retained_tiles: Vec<(ZoomLevel, XCoord, YCoord, SourceId)>
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct TilesetTile {
|
struct TilesetTile {
|
||||||
tileid: (ZoomLevel, XCoord, YCoord, LayerId),
|
tileid: (ZoomLevel, XCoord, YCoord, SourceId),
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
tile: Option<DynamicImage>,
|
tile: Option<DynamicImage>,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::mem;
|
||||||
use glow::{Buffer, PixelUnpackData, Program, Texture, VertexArray};
|
use glow::{Buffer, PixelUnpackData, Program, Texture, VertexArray};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, warn};
|
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;
|
use crate::map::Tileset;
|
||||||
|
|
||||||
pub struct MapRender {
|
pub struct MapRender {
|
||||||
|
@ -12,7 +12,7 @@ pub struct MapRender {
|
||||||
vao: VertexArray,
|
vao: VertexArray,
|
||||||
ebo: Buffer,
|
ebo: Buffer,
|
||||||
shader_program: Program,
|
shader_program: Program,
|
||||||
texture_cache: HashMap<(ZoomLevel, XCoord, YCoord, LayerId), Texture>
|
texture_cache: HashMap<(ZoomLevel, XCoord, YCoord, SourceId), Texture>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::cmp::PartialEq;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
|
@ -11,24 +12,25 @@ use crate::map::Tileset;
|
||||||
pub type ZoomLevel = usize;
|
pub type ZoomLevel = usize;
|
||||||
pub type XCoord = usize;
|
pub type XCoord = usize;
|
||||||
pub type YCoord = usize;
|
pub type YCoord = usize;
|
||||||
pub type LayerId = u64;
|
pub type SourceId = u64;
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Debug)]
|
#[derive(Default, Serialize, Deserialize, Debug)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LayerManager {
|
pub struct LayerManager {
|
||||||
pub registered_layers: HashMap<LayerId, LayerSource>,
|
pub registered_sources: HashMap<SourceId, LayerSource>,
|
||||||
pub active_layers: Vec<LayerId>,
|
pub active_layers: Vec<ActiveLayer>,
|
||||||
#[serde(skip)]
|
#[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
|
pub tile_cache_retention: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayerManager {
|
impl LayerManager {
|
||||||
pub fn register_layer(&mut self, layer: &LayerSource) {
|
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) {
|
pub fn unregister_layer(&mut self, layer_id: &SourceId) {
|
||||||
self.registered_layers.remove(layer_id);
|
self.registered_sources.remove(layer_id);
|
||||||
self.active_layers.retain(|u| u != layer_id);
|
self.active_layers.retain(|u| u.source_id != *layer_id);
|
||||||
}
|
}
|
||||||
pub fn register_builtin_layers(&mut self) {
|
pub fn register_builtin_layers(&mut self) {
|
||||||
let layers = [
|
let layers = [
|
||||||
|
@ -53,7 +55,7 @@ impl LayerManager {
|
||||||
|
|
||||||
pub fn remove_unregistered_layers(&mut self) {
|
pub fn remove_unregistered_layers(&mut self) {
|
||||||
// remove any
|
// 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) {
|
pub fn garbage_collect(&mut self, current_tileset: &mut Tileset) {
|
||||||
let tiles_onscreen = current_tileset.tiles.lock().unwrap();
|
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 {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", if self.promise.ready().is_some() { "<loaded texture>" } else { "<texture promise>" })
|
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::GuiRounding;
|
||||||
use egui::{emath, CursorIcon, Id, InnerResponse, LayerId, Order, Sense, Ui, UiBuilder};
|
use egui::{emath, CursorIcon, Id, InnerResponse, LayerId, Order, Sense, Ui, UiBuilder};
|
||||||
|
|
||||||
pub mod layer_selector;
|
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
pub mod shell;
|
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 std::collections::HashMap;
|
||||||
use egui::{Color32, CursorIcon, FontFamily, Frame, Id, Layout, Response, RichText, ScrollArea, Theme, Ui, vec2, Visuals, CornerRadius, Button, Margin, Label, Rect, Sense};
|
use crate::map::tiles::{ActiveLayer, LayerId, LayerManager, LayerSource, LayerTypeHint, SourceId};
|
||||||
use egui::epaint::Marginf;
|
|
||||||
use egui::frame::Prepared;
|
|
||||||
use tracing::debug;
|
|
||||||
use crate::map::tiles::{LayerManager, LayerTypeHint};
|
|
||||||
use crate::ui::tokens::DESIGN_TOKENS;
|
use crate::ui::tokens::DESIGN_TOKENS;
|
||||||
use crate::ui::UiExt;
|
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 {
|
fn outer_rect(f: &Prepared) -> Rect {
|
||||||
|
|
||||||
let content_rect = f.content_ui.min_rect();
|
let content_rect = f.content_ui.min_rect();
|
||||||
|
|
||||||
content_rect
|
content_rect + f.frame.inner_margin + Marginf::from(f.frame.stroke.width) + f.frame.outer_margin
|
||||||
|
|
||||||
+ 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, selected_layer: &mut Option<LayerId>) {
|
||||||
pub fn left_bar(ctx: &egui::Context, add_open: &mut bool, layer_manager: &mut LayerManager) {
|
|
||||||
|
|
||||||
let mut frame = Frame::side_top_panel(&ctx.style());
|
let mut frame = Frame::side_top_panel(&ctx.style());
|
||||||
frame.inner_margin.top = 4;
|
frame.inner_margin.top = 4;
|
||||||
frame.inner_margin.left = 0;
|
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")
|
egui::SidePanel::left("left_panel")
|
||||||
.frame(frame)
|
.frame(frame)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
pane_header(ui, "Layers", Some(egui_phosphor::regular::STACK), false, |ui| {
|
pane_header(
|
||||||
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
|
ui,
|
||||||
if ui.button(RichText::new(egui_phosphor::regular::PLUS).size(12.0)).clicked() {
|
"Layers",
|
||||||
*add_open = true;
|
Some(egui_phosphor::regular::STACK),
|
||||||
}
|
false,
|
||||||
});
|
|ui| {
|
||||||
|
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
|
||||||
for (idx, layer) in layer_manager.active_layers.iter().enumerate() {
|
if ui
|
||||||
ui.dnd_drag_source(
|
.button(RichText::new(egui_phosphor::regular::PLUS).size(12.0))
|
||||||
Id::new(idx as u64 + layer),
|
.clicked()
|
||||||
idx,
|
{
|
||||||
|ui| {
|
*add_open = true;
|
||||||
if ui.button("hello").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 {
|
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"))
|
egui::Modal::new(Id::new("add_source_modal"))
|
||||||
.frame(frame)
|
.frame(frame)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
pane_header(ui, "Add layer", Some(egui_phosphor::regular::STACK_PLUS), false, |ui| {
|
pane_header(
|
||||||
ui.style_mut().visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;
|
ui,
|
||||||
if ui.button(RichText::new(egui_phosphor::regular::X).size(12.0)).clicked() {
|
"Add layer",
|
||||||
*add_open = false;
|
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
|
// ghost button
|
||||||
|
|
||||||
let mut frame = Frame::new();
|
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| {
|
frame.content_ui.horizontal(|ui| {
|
||||||
let mut frame = Frame::new()
|
let mut frame = Frame::new().fill(
|
||||||
.fill(DESIGN_TOKENS.get_color("colors.base.4", if ui.style().visuals.dark_mode { Theme::Dark } else { Theme::Light }).unwrap());
|
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.top = 4;
|
||||||
frame.inner_margin.left = 8;
|
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.corner_radius = CornerRadius::from(4);
|
||||||
|
|
||||||
frame.show(ui, |ui| {
|
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 {
|
ui.label(RichText::new(icon).size(24.0));
|
||||||
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
|
});
|
||||||
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.label(RichText::new(icon).size(24.0));
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.label(®istered_layer.display_name);
|
ui.label(®istered_layer.display_name);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|
||||||
let type_hint = match registered_layer.type_hint {
|
let type_hint = match registered_layer.type_hint {
|
||||||
LayerTypeHint::Baselayer => "Baselayer",
|
LayerTypeHint::Baselayer => "Baselayer",
|
||||||
LayerTypeHint::RadarData => "Radar data",
|
LayerTypeHint::RadarData => "Radar data",
|
||||||
};
|
};
|
||||||
let icon = match registered_layer.type_hint {
|
let icon = match registered_layer.type_hint {
|
||||||
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
|
LayerTypeHint::Baselayer => {
|
||||||
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET
|
egui_phosphor::regular::MAP_TRIFOLD
|
||||||
|
}
|
||||||
|
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET,
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_lbl = format!("{icon} {type_hint}");
|
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| {
|
||||||
ui.label(RichText::new(type_lbl).weak());
|
ui.label(RichText::new(type_lbl).weak());
|
||||||
ui.add_space(ui.available_width());
|
ui.add_space(ui.available_width());
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
ui.allocate_ui_with_layout(
|
ui.allocate_ui_with_layout(
|
||||||
vec2(120.0, 20.0),
|
vec2(120.0, 20.0),
|
||||||
Layout::left_to_right(Align::Center),
|
Layout::left_to_right(Align::Center),
|
||||||
|ui| {
|
|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.add_space(ui.available_width());
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.allocate_ui_with_layout(
|
ui.allocate_ui_with_layout(
|
||||||
vec2(100.0, 20.0),
|
vec2(100.0, 20.0),
|
||||||
Layout::left_to_right(Align::Center),
|
Layout::left_to_right(Align::Center),
|
||||||
|ui| {
|
|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());
|
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());
|
let response = ui.allocate_rect(outer_rect(&frame), Sense::click_and_drag());
|
||||||
if response.hovered() {
|
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();
|
frame.frame.fill = DESIGN_TOKENS
|
||||||
ui.ctx().output_mut(|u| u.cursor_icon = CursorIcon::PointingHand);
|
.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() {
|
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);
|
debug!("{:?}", layer_manager);
|
||||||
*add_open = false;
|
*add_open = false;
|
||||||
}
|
}
|
||||||
frame.paint(ui);
|
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| {
|
fn draw_layer(ui: &mut Ui, layer: &mut ActiveLayer, registry: &HashMap<SourceId, LayerSource>, selected_layer: &mut Option<LayerId>) -> bool {
|
||||||
ui.horizontal(|ui| {
|
let mut retain = true;
|
||||||
let text = if let Some(icon) = icon {
|
|
||||||
RichText::new(format!("{} {}", icon, title))
|
ui.scope_builder(
|
||||||
} else {
|
UiBuilder::new()
|
||||||
RichText::new(title)
|
.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;
|
||||||
|
|
||||||
/*
|
let mut frame = frame.begin(ui);
|
||||||
fn layer(ui: &mut Ui, title: &str, icon: &str) {
|
{
|
||||||
ui.passthrough_dnd_drag_source(Id::new(title), title.to_owned(), |ui| {
|
frame.content_ui.horizontal(|ui| {
|
||||||
let mut frame = Frame::new();
|
ui.add(
|
||||||
//frame.fill = ui.style().visuals.hyperlink_color;
|
Label::new(
|
||||||
frame.inner_margin.left = 8;
|
RichText::new(egui_phosphor::regular::DOTS_SIX_VERTICAL)
|
||||||
frame.inner_margin.right = 8;
|
.size(12.0)
|
||||||
frame.inner_margin.top = 2;
|
.family(FontFamily::Name("icon-only".into()))
|
||||||
frame.inner_margin.bottom = 2;
|
.weak(),
|
||||||
let mut frame = frame.begin(ui);
|
)
|
||||||
{
|
.sense(Sense::empty())
|
||||||
frame.content_ui.horizontal(|ui| {
|
.selectable(false)
|
||||||
|
|
||||||
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()))
|
|
||||||
);
|
);
|
||||||
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)
|
.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;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
frame.allocate_space(ui);
|
||||||
let response = frame.allocate_space(ui);
|
|
||||||
if response.hovered() {
|
let response = ui.response();
|
||||||
frame.frame.fill = ;
|
|
||||||
ui.ctx().output_mut(|u| u.cursor_icon = CursorIcon::Grab);
|
if response.hovered() && !any_children_hovered {
|
||||||
}
|
frame.frame.fill = ui.visuals().widgets.active.weak_bg_fill;
|
||||||
frame.paint(ui);
|
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 top_bar;
|
||||||
pub mod footer;
|
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