pages are fun
This commit is contained in:
@@ -682,7 +682,7 @@ fn parse_layout() {
|
||||
[{q}{w}{e}{r}{t}{y}{u}{i}{o}{p}]
|
||||
[{a}{s}{d}{f}{g}{h}{j}{k}{l}]
|
||||
[{⇧:Modifier(Shift);1.5}{z}{x}{c}{v}{b}{n}{m}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
);
|
||||
assert!(layout_qwerty.is_ok());
|
||||
|
||||
@@ -691,7 +691,7 @@ fn parse_layout() {
|
||||
[{Q}{W}{E}{R}{T}{Y}{U}{I}{O}{P}]
|
||||
[{A}{S}{D}{F}{G}{H}{J}{K}{L}]
|
||||
[{⬆:Modifier(Shift);1.5}{Z}{X}{C}{V}{B}{N}{M}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
);
|
||||
assert!(layout_qwerty_shift_once.is_ok());
|
||||
|
||||
@@ -700,7 +700,7 @@ fn parse_layout() {
|
||||
[{Q}{W}{E}{R}{T}{Y}{U}{I}{O}{P}]
|
||||
[{A}{S}{D}{F}{G}{H}{J}{K}{L}]
|
||||
[{⮉:Modifier(Shift);1.5}{Z}{X}{C}{V}{B}{N}{M}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
);
|
||||
assert!(layout_qwerty_shift_hold.is_ok());
|
||||
|
||||
@@ -709,7 +709,7 @@ fn parse_layout() {
|
||||
[{1}{2}{3}{4}{5}{6}{7}{8}{9}{0}]
|
||||
[{@}{#}{$}{_}{%}{&}{-}{+}{(}{)}]
|
||||
[{=\\<:Layout(Signs_Mobile);1.5}{*}{\"}{\'}{\\:}{\\;}{!}{?}{<❌:Key(Backspace);1.5}]
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
);
|
||||
assert!(layout_numbers.is_ok());
|
||||
|
||||
@@ -717,7 +717,7 @@ fn parse_layout() {
|
||||
"Signs_Mobile
|
||||
[{~}{`}{|}{\\}{=}{^}{<}{>}{[}{]}]
|
||||
[{?123:Layout(Numeric_Mobile);1.5}{*}{\"}{\'}{\\:}{\\;}{!}{?}{<❌:Key(Backspace);1.5}]
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
);
|
||||
assert!(layout_symbols.is_ok());
|
||||
}
|
||||
@@ -829,7 +829,7 @@ impl Default for VirtualKeyboard {
|
||||
[{q}{w}{e}{r}{t}{y}{u}{i}{o}{p}]
|
||||
[{a}{s}{d}{f}{g}{h}{j}{k}{l}]
|
||||
[{⇧:Modifier(Shift);1.5}{z}{x}{c}{v}{b}{n}{m}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -839,7 +839,7 @@ impl Default for VirtualKeyboard {
|
||||
[{Q}{W}{E}{R}{T}{Y}{U}{I}{O}{P}]
|
||||
[{A}{S}{D}{F}{G}{H}{J}{K}{L}]
|
||||
[{⬆:Modifier(Shift);1.5}{Z}{X}{C}{V}{B}{N}{M}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{/}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{/}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
)
|
||||
.unwrap()
|
||||
.with_modifiers(ModState::Off, ModState::Once, ModState::Off);
|
||||
@@ -850,7 +850,7 @@ impl Default for VirtualKeyboard {
|
||||
[{Q}{W}{E}{R}{T}{Y}{U}{I}{O}{P}]
|
||||
[{A}{S}{D}{F}{G}{H}{J}{K}{L}]
|
||||
[{⮉:Modifier(Shift);1.5}{Z}{X}{C}{V}{B}{N}{M}{<❌:Key(Backspace);1.5}]
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{?123:Layout(Numeric_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
)
|
||||
.unwrap()
|
||||
.with_modifiers(ModState::Off, ModState::Hold, ModState::Off);
|
||||
@@ -860,7 +860,7 @@ impl Default for VirtualKeyboard {
|
||||
[{1}{2}{3}{4}{5}{6}{7}{8}{9}{0}{⇩:Close}]
|
||||
[{@}{#}{$}{_}{%}{&}{-}{+}{(}{)}]
|
||||
[{=\\<:Layout(Symbols_Mobile);1.5}{*}{\"}{\'}{\\:}{\\;}{!}{?}{<❌:Key(Backspace);1.5}]
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -868,7 +868,7 @@ impl Default for VirtualKeyboard {
|
||||
"Symbols_Mobile
|
||||
[{~}{`}{|}{\\}{=}{^}{<}{>}{[}{]}]
|
||||
[{?123:Layout(Numeric_Mobile);1.5}{*}{\"}{\'}{\\:}{\\;}{!}{?}{<❌:Key(Backspace);1.5}]
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{Space:Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
[{ABC:Layout(QWERTY_Mobile);2.0}{,}{ :Key(Space);3.0}{/}{.}{⮨:Key(Enter);2.0}]",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -946,7 +946,7 @@ impl VirtualKeyboard {
|
||||
|
||||
#[inline]
|
||||
/// Use provided spacing instead of context one
|
||||
pub fn with_butyon_spacing(mut self, spacing: Vec2) -> Self {
|
||||
pub fn with_button_spacing(mut self, spacing: Vec2) -> Self {
|
||||
self.button_s = Some(spacing);
|
||||
self
|
||||
}
|
||||
@@ -982,7 +982,7 @@ impl VirtualKeyboard {
|
||||
focused.is_some() && state.focus != focused
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
pub fn deactivate_next_frame(&mut self) {
|
||||
self.stopping = true;
|
||||
}
|
||||
|
||||
@@ -1086,13 +1086,6 @@ impl VirtualKeyboard {
|
||||
}
|
||||
|
||||
let vis = ui.style().noninteractive();
|
||||
ui.painter().rect(
|
||||
kbd_rect,
|
||||
vis.corner_radius,
|
||||
vis.weak_bg_fill,
|
||||
vis.bg_stroke,
|
||||
egui::StrokeKind::Middle,
|
||||
);
|
||||
|
||||
let draw_btn = |button: &Button, offset: &mut Vec2, ui: &mut Ui| {
|
||||
let kbd_min = kbd_rect.min.to_vec2();
|
||||
@@ -1185,7 +1178,7 @@ impl VirtualKeyboard {
|
||||
};
|
||||
match (response.clicked(), response.double_clicked(), action) {
|
||||
(true, _, KeyAction::Close) => {
|
||||
self.stop(); // ok so, this doesnt work
|
||||
self.deactivate_next_frame();
|
||||
}
|
||||
(true, _, KeyAction::Layout(name)) => self.switch_layout(&name),
|
||||
(true, false, KeyAction::Modifier(modifier)) => {
|
||||
|
||||
175
src/apad/mod.rs
175
src/apad/mod.rs
@@ -32,7 +32,10 @@ use bevy_egui::{
|
||||
};
|
||||
use bevy_mod_openxr::session::OxrSession;
|
||||
use bevy_pkv::{PersistentResourceAppExtensions, PkvStore};
|
||||
use egui::{Color32, Margin, PointerButton, Pos2};
|
||||
use egui::{
|
||||
Button, Color32, Layout, Margin, PointerButton, Pos2, Rect, TextEdit, Theme, Ui,
|
||||
scroll_area::ScrollSource,
|
||||
};
|
||||
use openxr::Path;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -46,7 +49,7 @@ use crate::{
|
||||
keyboard::VirtualKeyboard,
|
||||
otdipcplugin::{OtdIpcPlugin, PenButtons, PenDelta, PenPosition},
|
||||
},
|
||||
egui_pages::{Overview, overview},
|
||||
egui_pages::*,
|
||||
vrcontrollerplugin::{
|
||||
LeftController, LeftControllerActions, RightController, RightControllerActions,
|
||||
},
|
||||
@@ -101,6 +104,9 @@ impl Default for KneeboardPosition {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Serialize, Deserialize, Default)]
|
||||
pub struct KneeboardNotepad(String);
|
||||
|
||||
pub struct APadPlugin;
|
||||
impl Plugin for APadPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
@@ -109,10 +115,12 @@ impl Plugin for APadPlugin {
|
||||
.add_plugins(MaterialPlugin::<MyCustomMaterial>::default());
|
||||
|
||||
app.insert_resource(PkvStore::new("Avii", "Kneeboard"))
|
||||
.init_persistent_resource::<KneeboardPosition>();
|
||||
.init_persistent_resource::<KneeboardPosition>()
|
||||
.init_persistent_resource::<KneeboardNotepad>();
|
||||
|
||||
app.insert_resource(TabletSize(Vec2::new(210.0, 279.0)));
|
||||
app.insert_resource(TabletResolutionScale(2.25));
|
||||
// app.insert_resource(TabletResolutionScale(2.25));
|
||||
app.insert_resource(TabletResolutionScale(2.5));
|
||||
app.insert_resource(PointerImage(None));
|
||||
app.insert_resource(Name("".to_string()));
|
||||
app.insert_resource(KeyboardVisible(false));
|
||||
@@ -127,9 +135,11 @@ impl Plugin for APadPlugin {
|
||||
position_kneeboard,
|
||||
move_kneeboard,
|
||||
sync_camera_with_kneeboard,
|
||||
),
|
||||
)
|
||||
.chain(),
|
||||
)
|
||||
.add_systems(Update, pointer.in_set(EguiInputSet::InitReading))
|
||||
.add_systems(Update, update_pointer.in_set(EguiInputSet::WriteEguiEvents))
|
||||
.add_systems(Update, render_pointer)
|
||||
.add_systems(WorldspaceContextPass, update);
|
||||
}
|
||||
}
|
||||
@@ -158,29 +168,16 @@ fn setup_pointer(
|
||||
pointer.0 = Some(image);
|
||||
}
|
||||
|
||||
fn pointer(
|
||||
fn update_pointer(
|
||||
tablet_size: Res<TabletSize>,
|
||||
tablet_res: Res<TabletResolutionScale>,
|
||||
ent: Single<Entity, With<bevy_egui::EguiContext>>,
|
||||
mesh_material: Single<&MeshMaterial3d<MyCustomMaterial>, With<Kneeboard>>,
|
||||
mut pen_buttons: MessageReader<PenButtons>,
|
||||
mut pen_delta: MessageReader<PenDelta>,
|
||||
mut pen_position: MessageReader<PenPosition>,
|
||||
mut input_writer: MessageWriter<EguiInputEvent>,
|
||||
mut materials: ResMut<Assets<MyCustomMaterial>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut pointer: ResMut<PointerImage>,
|
||||
mut pen_button_pressed: ResMut<WasPenButtonPressed>,
|
||||
) {
|
||||
let Some(ref mut pointer) = pointer.0 else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(image) = images.get_mut(&*pointer) else {
|
||||
return;
|
||||
};
|
||||
|
||||
image.clear(&[0u8; 4]);
|
||||
for pos in pen_position.read() {
|
||||
let x = pos.x * tablet_size.x * **tablet_res;
|
||||
let y = pos.y * tablet_size.y * **tablet_res;
|
||||
@@ -206,6 +203,7 @@ fn pointer(
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is making me drop inputs or something
|
||||
if **pen_button_pressed != button.tip() {
|
||||
input_writer.write(EguiInputEvent {
|
||||
context: *ent,
|
||||
@@ -219,7 +217,31 @@ fn pointer(
|
||||
**pen_button_pressed = button.tip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_pointer(
|
||||
tablet_size: Res<TabletSize>,
|
||||
tablet_res: Res<TabletResolutionScale>,
|
||||
mesh_material: Single<&MeshMaterial3d<MyCustomMaterial>, With<Kneeboard>>,
|
||||
mut pen_position: MessageReader<PenPosition>,
|
||||
mut materials: ResMut<Assets<MyCustomMaterial>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut pointer: ResMut<PointerImage>,
|
||||
) {
|
||||
let Some(ref mut pointer) = pointer.0 else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(image) = images.get_mut(&*pointer) else {
|
||||
return;
|
||||
};
|
||||
|
||||
image.clear(&[0u8; 4]);
|
||||
|
||||
for pos in pen_position.read() {
|
||||
let x = pos.x * tablet_size.x * **tablet_res;
|
||||
let y = pos.y * tablet_size.y * **tablet_res;
|
||||
draw_pointer(image, x, y);
|
||||
|
||||
if let Some(material) = materials.get_mut(mesh_material.0.id()) {
|
||||
@@ -354,44 +376,109 @@ fn update(
|
||||
mut input: Single<&mut bevy_egui::EguiInput>,
|
||||
mut keyboard: Single<&mut Keyboard>,
|
||||
|
||||
mut notepad: ResMut<KneeboardNotepad>,
|
||||
|
||||
mo: Res<Overview>,
|
||||
sr: Res<Sitrep>,
|
||||
pr: Res<PilotRoster>,
|
||||
pe: Res<PackageElements>,
|
||||
ta: Res<ThreatAnalysis>,
|
||||
sp: Res<Steerpoints>,
|
||||
cl: Res<Commladder>,
|
||||
or: Res<Ordnance>,
|
||||
wt: Res<Weather>,
|
||||
su: Res<Support>,
|
||||
ro: Res<RulesOfEngagement>,
|
||||
ep: Res<Emergency>,
|
||||
) {
|
||||
let bgcolor = egui::containers::Frame {
|
||||
fill: egui::Color32::from_rgb(43, 44, 47),
|
||||
inner_margin: Margin {
|
||||
left: 15,
|
||||
right: 15,
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
},
|
||||
ctx.get_mut().options_mut(|opt| {
|
||||
opt.input_options.max_click_dist = 24.0;
|
||||
opt.input_options.max_click_duration = 1.0;
|
||||
});
|
||||
|
||||
let margins = egui::containers::Frame {
|
||||
inner_margin: Margin::same(15),
|
||||
..Default::default()
|
||||
};
|
||||
let focus = keyboard.is_active(ctx.get_mut());
|
||||
|
||||
let height = if focus { 200.0 } else { 0.0 };
|
||||
|
||||
egui::containers::CentralPanel::default()
|
||||
.frame(bgcolor)
|
||||
.show(ctx.get_mut(), |ui| {
|
||||
egui::containers::TopBottomPanel::bottom("bottom_panel")
|
||||
.resizable(false)
|
||||
.height_range(egui::Rangef::new(0., height))
|
||||
.show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
keyboard.show(ui);
|
||||
});
|
||||
egui::containers::CentralPanel::default().show(ctx.get_mut(), |ui| {
|
||||
egui::containers::TopBottomPanel::bottom("bottom_panel")
|
||||
.resizable(false)
|
||||
.height_range(egui::Rangef::new(0., height))
|
||||
.show_inside(ui, |ui| {
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::Center), |ui| {
|
||||
keyboard.show(ui);
|
||||
});
|
||||
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
overview(ui, mo);
|
||||
|
||||
// ..
|
||||
});
|
||||
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
let rect = ui
|
||||
.add(TextEdit::multiline(&mut notepad.0).margin(Margin::same(8)))
|
||||
.rect;
|
||||
global_theme_preference_switch(ui, &rect);
|
||||
});
|
||||
|
||||
egui::containers::CentralPanel::default()
|
||||
.frame(margins)
|
||||
.show_inside(ui, |ui| {
|
||||
egui::ScrollArea::vertical()
|
||||
// .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||
.scroll_source(ScrollSource::MOUSE_WHEEL | ScrollSource::SCROLL_BAR)
|
||||
.show(ui, |ui| {
|
||||
overview(ui, &mo);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
sitrep(ui, &sr);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
pilot_roster(ui, &pr);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
package_elements(ui, &pe);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
threat_analysis(ui, &ta);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
steerpoints(ui, &sp);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
commladder(ui, &cl);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
ordnance(ui, &or, &mo);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
weather(ui, &wt);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
support(ui, &su);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
rulesofengagement(ui, &ro);
|
||||
ui.add(egui::Separator::default().grow(8.0));
|
||||
emergency(ui, &ep);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
keyboard.bump_events(ctx.get_mut(), &mut input.0);
|
||||
}
|
||||
|
||||
pub fn global_theme_preference_switch(ui: &mut Ui, rect: &egui::Rect) {
|
||||
let theme = ui.ctx().theme();
|
||||
let icon = if theme == Theme::Dark { "☀" } else { "🌙" };
|
||||
let new_theme = if theme == Theme::Dark {
|
||||
Theme::Light
|
||||
} else {
|
||||
Theme::Dark
|
||||
};
|
||||
|
||||
let size = 16.;
|
||||
|
||||
let min = egui::pos2(rect.max.x - size, rect.min.y - 2.);
|
||||
let max = egui::pos2(rect.max.x, size - 2.);
|
||||
|
||||
let new_rect = Rect::from_min_max(min, max);
|
||||
|
||||
if ui.put(new_rect, Button::new(icon).frame(false)).clicked() {
|
||||
ui.ctx().set_theme(new_theme);
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_camera_with_kneeboard(
|
||||
kneeboard: Query<&Transform, With<Kneeboard>>,
|
||||
mut cameras: Query<&mut Transform, (With<MainCamera>, Without<Kneeboard>)>,
|
||||
@@ -482,8 +569,6 @@ fn move_kneeboard(
|
||||
if let Ok(trigger_state) = left.squeeze_click.state(&session, Path::NULL)
|
||||
&& trigger_state.current_state
|
||||
{
|
||||
dbg!("squeeze triggered");
|
||||
|
||||
let Ok(transform) = left_transform.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
87
src/egui_pages/commladder.rs
Normal file
87
src/egui_pages/commladder.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Default)]
|
||||
pub struct InnerCommladder {
|
||||
pub agency: String,
|
||||
pub callsign: String,
|
||||
pub uhf: String,
|
||||
pub vhf: String,
|
||||
pub notes: String,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Commladder(pub Vec<InnerCommladder>);
|
||||
|
||||
impl From<&bms_briefing_parser::Comm<'_>> for InnerCommladder {
|
||||
fn from(value: &bms_briefing_parser::Comm) -> Self {
|
||||
Self {
|
||||
agency: value.agency.to_string(),
|
||||
callsign: value.callsign.unwrap_or_default().to_string(),
|
||||
uhf: value.uhf.unwrap_or_default().to_string(),
|
||||
vhf: value.vhf.unwrap_or_default().to_string(),
|
||||
notes: value.notes.unwrap_or_default().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commladder(ui: &mut Ui, data: &Res<Commladder>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table(
|
||||
ui,
|
||||
"Comm Ladder",
|
||||
&[
|
||||
("Agency", Column::remainder()),
|
||||
("Callsign", Column::remainder()),
|
||||
("UFH [ch]", Column::remainder()),
|
||||
("VHF [ch]", Column::remainder()),
|
||||
("Notes", Column::remainder()),
|
||||
],
|
||||
|mut body| {
|
||||
for commladder in data.0.iter() {
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&commladder.agency))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&commladder.callsign))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&commladder.uhf))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&commladder.vhf))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&commladder.notes))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
25
src/egui_pages/emergency.rs
Normal file
25
src/egui_pages/emergency.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Layout, Ui};
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Emergency(String);
|
||||
|
||||
impl From<bms_briefing_parser::Emergency> for Emergency {
|
||||
fn from(value: bms_briefing_parser::Emergency) -> Self {
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emergency(ui: &mut Ui, data: &Res<Emergency>) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("Emergency Procedures");
|
||||
});
|
||||
|
||||
// let data = RichText::from(&**data);
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
ui.label(&data.0);
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,31 @@
|
||||
mod commladder;
|
||||
mod emergency;
|
||||
mod ordnance;
|
||||
mod overview;
|
||||
mod package_elements;
|
||||
mod pilot_roster;
|
||||
mod roe;
|
||||
mod sitrep;
|
||||
mod steerpoints;
|
||||
mod support;
|
||||
mod threat_analysis;
|
||||
mod weather;
|
||||
|
||||
pub use commladder::*;
|
||||
pub use emergency::*;
|
||||
pub use ordnance::*;
|
||||
pub use overview::*;
|
||||
pub use package_elements::*;
|
||||
pub use pilot_roster::*;
|
||||
pub use roe::*;
|
||||
pub use sitrep::*;
|
||||
pub use steerpoints::*;
|
||||
pub use support::*;
|
||||
pub use threat_analysis::*;
|
||||
pub use weather::*;
|
||||
|
||||
use egui::Ui;
|
||||
use egui_extras::{Column, TableBody, TableBuilder};
|
||||
use std::{fs::File, io::Read, path::PathBuf};
|
||||
|
||||
use bevy::{
|
||||
@@ -12,7 +38,6 @@ use bevy::{
|
||||
};
|
||||
use encoding_rs::WINDOWS_1252;
|
||||
use encoding_rs_io::DecodeReaderBytesBuilder;
|
||||
pub use overview::*;
|
||||
|
||||
use crate::BriefingPath;
|
||||
|
||||
@@ -34,9 +59,21 @@ impl BmsPlugin {
|
||||
|
||||
impl Plugin for BmsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<Overview>();
|
||||
app.init_resource::<RawBriefing>();
|
||||
|
||||
app.init_resource::<Overview>();
|
||||
app.init_resource::<Sitrep>();
|
||||
app.init_resource::<PilotRoster>();
|
||||
app.init_resource::<PackageElements>();
|
||||
app.init_resource::<ThreatAnalysis>();
|
||||
app.init_resource::<Steerpoints>();
|
||||
app.init_resource::<Commladder>();
|
||||
app.init_resource::<Ordnance>();
|
||||
app.init_resource::<Weather>();
|
||||
app.init_resource::<Support>();
|
||||
app.init_resource::<RulesOfEngagement>();
|
||||
app.init_resource::<Emergency>();
|
||||
|
||||
app.insert_resource(BriefingPath(self.briefing_path.clone()));
|
||||
|
||||
let mut timer = Timer::from_seconds(5.0, TimerMode::Repeating);
|
||||
@@ -52,7 +89,18 @@ fn update_briefing(
|
||||
briefing_path: Res<BriefingPath>,
|
||||
mut timer: ResMut<RefreshTimer>,
|
||||
mut briefing: ResMut<RawBriefing>,
|
||||
mut overview: ResMut<Overview>,
|
||||
mut mo: ResMut<Overview>,
|
||||
mut sr: ResMut<Sitrep>,
|
||||
mut pr: ResMut<PilotRoster>,
|
||||
mut pe: ResMut<PackageElements>,
|
||||
mut ta: ResMut<ThreatAnalysis>,
|
||||
mut sp: ResMut<Steerpoints>,
|
||||
mut cl: ResMut<Commladder>,
|
||||
mut or: ResMut<Ordnance>,
|
||||
mut wt: ResMut<Weather>,
|
||||
mut su: ResMut<Support>,
|
||||
mut ro: ResMut<RulesOfEngagement>,
|
||||
mut ep: ResMut<Emergency>,
|
||||
) {
|
||||
if !timer.0.tick(time.delta()).just_finished() {
|
||||
return;
|
||||
@@ -71,10 +119,84 @@ fn update_briefing(
|
||||
.read_to_string(&mut buf)
|
||||
.unwrap();
|
||||
|
||||
if briefing.0 != buf {
|
||||
*overview = bms_briefing_parser::Overview::from_briefing(&buf).into();
|
||||
let buf = buf.replace(|c: char| !c.is_ascii(), "");
|
||||
|
||||
briefing.0 = buf;
|
||||
println!("Updated...");
|
||||
if briefing.0 == buf {
|
||||
return;
|
||||
}
|
||||
|
||||
*mo = bms_briefing_parser::Overview::from_briefing(&buf).into();
|
||||
*sr = bms_briefing_parser::Sitrep::from_briefing(&buf).into();
|
||||
*pr = PilotRoster(
|
||||
bms_briefing_parser::PilotRoster::from_briefing(&buf)
|
||||
.iter()
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<InnerPilotRoster>>(),
|
||||
);
|
||||
*pe = PackageElements(
|
||||
bms_briefing_parser::PackageElement::from_briefing(&buf)
|
||||
.iter()
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<InnerPackageElement>>(),
|
||||
);
|
||||
*ta = bms_briefing_parser::ThreatAnalysis::from_briefing(&buf).into();
|
||||
*sp = Steerpoints(
|
||||
bms_briefing_parser::Steerpoint::from_briefing(&buf)
|
||||
.iter()
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<InnerSteerpoint>>(),
|
||||
);
|
||||
*cl = Commladder(
|
||||
bms_briefing_parser::Comm::from_briefing(&buf)
|
||||
.iter()
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<InnerCommladder>>(),
|
||||
);
|
||||
*or = bms_briefing_parser::Ordnance::from_briefing(&buf).into();
|
||||
*wt = bms_briefing_parser::Weather::from_briefing(&buf).into();
|
||||
*su = bms_briefing_parser::Support::from_briefing(&buf).into();
|
||||
*ro = bms_briefing_parser::RulesOfEngagement::from_briefing(&buf).into();
|
||||
*ep = bms_briefing_parser::Emergency::from_briefing(&buf).into();
|
||||
|
||||
briefing.0 = buf;
|
||||
}
|
||||
|
||||
pub(super) fn table<F>(ui: &mut Ui, name: &str, cols: &[(&str, Column)], add_contents: F)
|
||||
where
|
||||
F: for<'b> FnOnce(TableBody<'b>),
|
||||
{
|
||||
if !name.starts_with('_') {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading(name);
|
||||
});
|
||||
}
|
||||
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
egui::ScrollArea::horizontal()
|
||||
.id_salt(format!("{}_scroll", name))
|
||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||
.show(ui, |ui| {
|
||||
let mut builder = TableBuilder::new(ui)
|
||||
.id_salt(format!("{}_table", name))
|
||||
.min_scrolled_height(f32::INFINITY)
|
||||
.striped(true);
|
||||
|
||||
for (_, col) in cols {
|
||||
builder = builder.column(*col);
|
||||
}
|
||||
|
||||
builder
|
||||
.header(text_height, |mut header| {
|
||||
for (name, _) in cols {
|
||||
header.col(|ui| {
|
||||
ui.strong(*name);
|
||||
});
|
||||
}
|
||||
})
|
||||
.body(add_contents);
|
||||
});
|
||||
}
|
||||
|
||||
77
src/egui_pages/ordnance.rs
Normal file
77
src/egui_pages/ordnance.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::Ui;
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Ordnance {
|
||||
pub flights: Vec<Vec<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl From<bms_briefing_parser::Ordnance<'_>> for Ordnance {
|
||||
fn from(value: bms_briefing_parser::Ordnance) -> Self {
|
||||
Self {
|
||||
flights: convert(value.flights.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ordnance(ui: &mut Ui, data: &Res<Ordnance>, overview: &Res<super::Overview>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
let mut cols = Vec::new();
|
||||
|
||||
for _ in &data.flights {
|
||||
cols.push(("", Column::remainder()));
|
||||
}
|
||||
|
||||
table(ui, "Ordnance", &cols, |mut body| {
|
||||
for group in &data.flights {
|
||||
body.row(text_height, |mut row| {
|
||||
for (i, ord) in group.iter().enumerate() {
|
||||
row.col(|ui| {
|
||||
let mut ord = ord.iter().enumerate();
|
||||
let name = ord.next().unwrap().1;
|
||||
table(
|
||||
ui,
|
||||
&format!("_{}", i),
|
||||
&[(name, Column::remainder())],
|
||||
|mut inner| {
|
||||
for (_, weap) in ord {
|
||||
inner.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
if name.starts_with(&overview.callsign) {
|
||||
ui.label(weap);
|
||||
} else {
|
||||
ui.weak(weap);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// jesus
|
||||
fn convert(input: Vec<Vec<Vec<&str>>>) -> Vec<Vec<Vec<String>>> {
|
||||
input
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|v| v.into_iter().map(String::from).collect())
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -43,31 +43,25 @@ impl From<bms_briefing_parser::Overview<'_>> for Overview {
|
||||
}
|
||||
}
|
||||
|
||||
// use egui::Response;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn overview(ui: &mut Ui, overview: Res<Overview>) {
|
||||
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
|
||||
pub fn overview(ui: &mut Ui, data: &Res<Overview>) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading(format!("Mission Overview - {}", overview.callsign));
|
||||
ui.heading(format!("Mission Overview - {}", data.callsign));
|
||||
});
|
||||
|
||||
let table = TableBuilder::new(ui)
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder());
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table
|
||||
TableBuilder::new(ui)
|
||||
.id_salt("overview_table")
|
||||
.striped(true)
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
.header(20.0, |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("Mission Type");
|
||||
@@ -85,16 +79,21 @@ pub fn overview(ui: &mut Ui, overview: Res<Overview>) {
|
||||
.body(|mut body| {
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.mission_type).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.mission_type).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(format!("{}", overview.package_id)).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(
|
||||
Label::new(format!("{}", data.package_id))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap),
|
||||
);
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.package_description).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(
|
||||
Label::new(&data.package_description).wrap_mode(egui::TextWrapMode::Wrap),
|
||||
);
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.package_mission).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.package_mission).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,16 +114,16 @@ pub fn overview(ui: &mut Ui, overview: Res<Overview>) {
|
||||
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.target_area).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.target_area).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.time_on_target).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.time_on_target).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.sunrise).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.sunrise).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add(Label::new(&overview.sunset).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
ui.add(Label::new(&data.sunset).wrap_mode(egui::TextWrapMode::Wrap));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
122
src/egui_pages/package_elements.rs
Normal file
122
src/egui_pages/package_elements.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InnerPackageElement {
|
||||
pub primary: bool,
|
||||
pub callsign: String,
|
||||
pub flight: String,
|
||||
pub role: String,
|
||||
pub aircraft: String,
|
||||
pub task: String,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct PackageElements(pub Vec<InnerPackageElement>);
|
||||
|
||||
impl From<&bms_briefing_parser::PackageElement> for InnerPackageElement {
|
||||
fn from(value: &bms_briefing_parser::PackageElement) -> Self {
|
||||
Self {
|
||||
primary: value.is_primary,
|
||||
callsign: value.callsign.to_string(),
|
||||
flight: value.flight.to_string(),
|
||||
role: value.role.to_string(),
|
||||
aircraft: value.aircraft.to_string(),
|
||||
task: value.task.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_elements(ui: &mut Ui, data: &Res<PackageElements>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table(
|
||||
ui,
|
||||
"Package Elements",
|
||||
&[
|
||||
("#", Column::auto_with_initial_suggestion(0.0)),
|
||||
("Callsign", Column::auto_with_initial_suggestion(0.0)),
|
||||
(
|
||||
"Flight #",
|
||||
Column::auto_with_initial_suggestion(0.0).at_least(60.),
|
||||
),
|
||||
(
|
||||
"Role",
|
||||
Column::auto_with_initial_suggestion(0.0).at_least(60.),
|
||||
),
|
||||
("Aircraft", Column::auto_with_initial_suggestion(0.0)),
|
||||
("Task", Column::remainder()),
|
||||
],
|
||||
|mut body| {
|
||||
for (index, roster) in data.0.iter().enumerate() {
|
||||
if roster.callsign.is_empty() {
|
||||
continue;
|
||||
}
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(format!("{}", index + 1));
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text)
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.callsign);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text)
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.flight);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.role);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.aircraft);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text)
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.task);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
101
src/egui_pages/pilot_roster.rs
Normal file
101
src/egui_pages/pilot_roster.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InnerPilotRoster {
|
||||
pub primary: bool,
|
||||
pub callsign: String,
|
||||
pub lead: String,
|
||||
pub wing: String,
|
||||
pub element: String,
|
||||
pub four: String,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct PilotRoster(pub Vec<InnerPilotRoster>);
|
||||
|
||||
impl From<&bms_briefing_parser::PilotRoster<'_>> for InnerPilotRoster {
|
||||
fn from(value: &bms_briefing_parser::PilotRoster) -> Self {
|
||||
Self {
|
||||
primary: value.primary,
|
||||
callsign: value.callsign.to_string(),
|
||||
lead: value.lead.to_string(),
|
||||
wing: value.wing.to_string(),
|
||||
element: value.element.to_string(),
|
||||
four: value.four.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pilot_roster(ui: &mut Ui, data: &Res<PilotRoster>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table(
|
||||
ui,
|
||||
"Pilot Roster",
|
||||
&[
|
||||
("Callsign", Column::remainder()),
|
||||
("Lead", Column::remainder()),
|
||||
("Wing", Column::remainder()),
|
||||
("Element", Column::remainder()),
|
||||
("Four", Column::remainder()),
|
||||
],
|
||||
|mut body| {
|
||||
for roster in data.0.iter() {
|
||||
if roster.callsign.is_empty() {
|
||||
continue;
|
||||
}
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.callsign);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.lead);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.wing);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.element);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
let mut text = RichText::from(&roster.four);
|
||||
if roster.primary {
|
||||
text = text.strong();
|
||||
}
|
||||
Label::new(text).wrap_mode(egui::TextWrapMode::Wrap).ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
29
src/egui_pages/roe.rs
Normal file
29
src/egui_pages/roe.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Layout, Ui};
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct RulesOfEngagement(String);
|
||||
|
||||
impl From<bms_briefing_parser::RulesOfEngagement> for RulesOfEngagement {
|
||||
fn from(value: bms_briefing_parser::RulesOfEngagement) -> Self {
|
||||
Self(value.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rulesofengagement(ui: &mut Ui, data: &Res<RulesOfEngagement>) {
|
||||
if data.0.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("Rules of Engagement");
|
||||
});
|
||||
|
||||
// let data = RichText::from(&**data);
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
ui.label(&data.0);
|
||||
});
|
||||
}
|
||||
25
src/egui_pages/sitrep.rs
Normal file
25
src/egui_pages/sitrep.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Layout, Ui};
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Sitrep(String);
|
||||
|
||||
impl From<bms_briefing_parser::Sitrep> for Sitrep {
|
||||
fn from(value: bms_briefing_parser::Sitrep) -> Self {
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sitrep(ui: &mut Ui, data: &Res<Sitrep>) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("Situation Report");
|
||||
});
|
||||
|
||||
// let data = RichText::from(&**data);
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
ui.label(&data.0);
|
||||
});
|
||||
}
|
||||
125
src/egui_pages/steerpoints.rs
Normal file
125
src/egui_pages/steerpoints.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Default)]
|
||||
pub struct InnerSteerpoint {
|
||||
pub steerpoint: usize,
|
||||
pub description: String,
|
||||
pub time: String,
|
||||
pub distance: f64,
|
||||
pub heading: usize,
|
||||
pub cas: usize,
|
||||
pub altitude: String,
|
||||
pub action: String,
|
||||
pub form: String,
|
||||
pub comments: String,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Steerpoints(pub Vec<InnerSteerpoint>);
|
||||
|
||||
impl From<&bms_briefing_parser::Steerpoint<'_>> for InnerSteerpoint {
|
||||
fn from(value: &bms_briefing_parser::Steerpoint) -> Self {
|
||||
Self {
|
||||
steerpoint: value.steerpoint,
|
||||
description: value.description.unwrap_or_default().to_string(),
|
||||
time: value.time.unwrap_or_default().to_string(),
|
||||
distance: value.distance.unwrap_or_default(),
|
||||
heading: value.heading.unwrap_or_default(),
|
||||
cas: value.cas.unwrap_or_default(),
|
||||
altitude: value.altitude.unwrap_or_default().to_string(),
|
||||
action: value.action.unwrap_or_default().to_string(),
|
||||
form: value.form.unwrap_or_default().to_string(),
|
||||
comments: value.comments.unwrap_or_default().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn steerpoints(ui: &mut Ui, data: &Res<Steerpoints>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table(
|
||||
ui,
|
||||
"Steerpoints",
|
||||
&[
|
||||
("#", Column::remainder()),
|
||||
("Desc", Column::remainder()),
|
||||
("Time", Column::remainder()),
|
||||
("Dist", Column::remainder()),
|
||||
("HNG", Column::remainder()),
|
||||
("ALT", Column::remainder()),
|
||||
("Action", Column::remainder()),
|
||||
("Formation", Column::remainder()),
|
||||
("Comment", Column::remainder()),
|
||||
],
|
||||
|mut body| {
|
||||
for steerpoint in data.0.iter() {
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(format!("{}", steerpoint.steerpoint)))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.description))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.time))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(format!("{}nm", steerpoint.distance)))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(format!("{}°", steerpoint.heading)))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.altitude))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.action))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.form))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&steerpoint.comments))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
87
src/egui_pages/support.rs
Normal file
87
src/egui_pages/support.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InnerSupport {
|
||||
pub callsign: String,
|
||||
pub _type: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Support(pub Vec<InnerSupport>);
|
||||
|
||||
impl From<bms_briefing_parser::Support<'_>> for Support {
|
||||
fn from(value: bms_briefing_parser::Support) -> Self {
|
||||
let mut values = value.0.iter();
|
||||
let _ = values.next(); // headers, dont need them
|
||||
|
||||
let mut out = Vec::new();
|
||||
|
||||
for w in values {
|
||||
let mut w = w.iter();
|
||||
if w.len() == 1 {
|
||||
out.push(InnerSupport {
|
||||
callsign: String::new(),
|
||||
_type: String::new(),
|
||||
description: w.next().unwrap().to_string(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push(InnerSupport {
|
||||
callsign: w.next().unwrap().to_string(),
|
||||
_type: w.next().unwrap().to_string(),
|
||||
description: w.next().unwrap().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Self(out)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn support(ui: &mut Ui, data: &Res<Support>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
table(
|
||||
ui,
|
||||
"Support",
|
||||
&[
|
||||
("Callsign", Column::auto()),
|
||||
("Type", Column::auto()),
|
||||
("Description", Column::remainder()),
|
||||
],
|
||||
|mut body| {
|
||||
for support in data.0.iter() {
|
||||
body.row(text_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&support.callsign))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&support._type))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(&support.description))
|
||||
.wrap_mode(egui::TextWrapMode::Wrap)
|
||||
.ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
25
src/egui_pages/threat_analysis.rs
Normal file
25
src/egui_pages/threat_analysis.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Layout, Ui};
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct ThreatAnalysis(String);
|
||||
|
||||
impl From<bms_briefing_parser::ThreatAnalysis<'_>> for ThreatAnalysis {
|
||||
fn from(value: bms_briefing_parser::ThreatAnalysis) -> Self {
|
||||
Self(value.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn threat_analysis(ui: &mut Ui, data: &Res<ThreatAnalysis>) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("Threat Analysis");
|
||||
});
|
||||
|
||||
// let data = RichText::from(&**data);
|
||||
ui.with_layout(Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
ui.label(&data.0);
|
||||
});
|
||||
}
|
||||
55
src/egui_pages/weather.rs
Normal file
55
src/egui_pages/weather.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use bevy::{
|
||||
ecs::{resource::Resource, system::Res},
|
||||
prelude::{Deref, DerefMut},
|
||||
};
|
||||
use egui::{Label, RichText, Ui, Widget};
|
||||
use egui_extras::*;
|
||||
|
||||
use crate::egui_pages::table;
|
||||
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Weather(pub Vec<Vec<String>>);
|
||||
|
||||
impl From<bms_briefing_parser::Weather<'_>> for Weather {
|
||||
fn from(value: bms_briefing_parser::Weather) -> Self {
|
||||
Self(
|
||||
value
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|inner| inner.into_iter().map(|s| s.to_string()).collect())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn weather(ui: &mut Ui, data: &Res<Weather>) {
|
||||
let text_height = egui::TextStyle::Body
|
||||
.resolve(ui.style())
|
||||
.size
|
||||
.max(ui.spacing().interact_size.y);
|
||||
|
||||
let mut data = data.0.iter();
|
||||
|
||||
let Some(headers) = data.next() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut cols = Vec::new();
|
||||
for header in headers {
|
||||
cols.push((header.as_str(), Column::remainder()));
|
||||
}
|
||||
|
||||
table(ui, "Weather", &cols, |mut body| {
|
||||
for rows in data {
|
||||
body.row(text_height, |mut row| {
|
||||
for col in rows {
|
||||
row.col(|ui| {
|
||||
Label::new(RichText::from(col))
|
||||
.wrap_mode(egui::TextWrapMode::Extend)
|
||||
.ui(ui);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user