Skip to content

Commit 9110afd

Browse files
authored
Merge pull request #15 from sapessi/ui
UI Updates to resolve multiple issues
2 parents 0f54f74 + 1bc82c2 commit 9110afd

File tree

10 files changed

+298
-79
lines changed

10 files changed

+298
-79
lines changed

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ snafu = "0.8.5"
1717
egui_plot = { git="https://github.com/sapessi/egui_plot.git", branch="gradient_line" }
1818
iracing = { git="https://github.com/LeoAdamek/iracing.rs", features = [ "telemetry"] }
1919
itertools = "0.14.0"
20+
dirs = "6.0.0"

assets/alert-fill.png

3.54 KB
Loading

assets/layout-horizontal-fill.png

2.57 KB
Loading

assets/layout-vertical-fill.png

2.98 KB
Loading

src/live/alerts_view.rs

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,92 @@
1-
use egui::{Align, Id, Image, Layout, Sense, ViewportCommand};
1+
use egui::{Align, Id, Image, ImageButton, Layout, Sense, ViewportCommand};
22

33
use crate::telemetry::TelemetryAnnotation;
44

5-
use super::LiveTelemetryApp;
5+
use super::{config::AlertsLayout, LiveTelemetryApp};
66

77
impl LiveTelemetryApp {
88
pub(crate) fn alerts_view(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
9-
egui::CentralPanel::default().show(ctx, |ui| {
10-
ui.with_layout(Layout::top_down(Align::TOP), |ui| {
11-
// load warning based on telemetry data
12-
let mut abs_image = egui::include_image!("../../assets/brake-green.png");
13-
let mut shift_image = egui::include_image!("../../assets/shift-orange.png");
14-
let mut wheelspin_image = egui::include_image!("../../assets/wheelspin-green.png");
15-
if let Some(back) = self.telemetry_points.back() {
16-
if back.brake > 0.4 && !back.abs_active {
17-
abs_image = egui::include_image!("../../assets/brake-orange.png");
18-
}
19-
if back.abs_active {
20-
abs_image = egui::include_image!("../../assets/brake-red.png");
21-
}
22-
23-
if back.cur_rpm > back.car_shift_ideal_rpm - 100.
24-
&& back.cur_rpm < back.car_shift_ideal_rpm + 100.
25-
{
26-
shift_image = egui::include_image!("../../assets/shift-green.png");
27-
}
28-
29-
if let Some(TelemetryAnnotation::Bool(true)) = back.annotations.get("wheelspin")
30-
{
31-
wheelspin_image = egui::include_image!("../../assets/wheelspin-red.png");
32-
}
33-
}
34-
35-
ui.label("ABS");
36-
ui.add(Image::new(abs_image));
37-
if ui
38-
.interact(ui.max_rect(), Id::new("window-drag"), Sense::drag())
39-
.dragged()
40-
{
9+
egui::TopBottomPanel::top("Controls")
10+
.min_height(30.)
11+
.show(ctx, |ui| {
12+
let drag_sense = ui.interact(ui.max_rect(), Id::new("window-drag"), Sense::drag());
13+
if drag_sense.dragged() {
4114
ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag);
4215
}
43-
ui.separator();
44-
ui.label("Shift");
45-
ui.add(Image::new(shift_image));
46-
47-
ui.separator();
48-
ui.label("Traction");
49-
ui.add(Image::new(wheelspin_image));
50-
51-
if ui
52-
.interact(ui.max_rect(), Id::new("window-drag"), Sense::drag())
53-
.dragged()
54-
{
55-
ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag);
16+
if drag_sense.drag_stopped() {
17+
if let Some(outer_rect) = ui.input(|is| is.viewport().outer_rect) {
18+
self.app_config.alert_window_position = outer_rect.min.into();
19+
};
20+
}
21+
match self.app_config.alerts_layout {
22+
AlertsLayout::Vertical => {
23+
if ui
24+
.add(ImageButton::new(egui::include_image!(
25+
"../../assets/layout-horizontal-fill.png"
26+
)))
27+
.clicked()
28+
{
29+
self.app_config.alerts_layout = AlertsLayout::Horizontal;
30+
}
31+
}
32+
AlertsLayout::Horizontal => {
33+
if ui
34+
.add(ImageButton::new(egui::include_image!(
35+
"../../assets/layout-vertical-fill.png"
36+
)))
37+
.clicked()
38+
{
39+
self.app_config.alerts_layout = AlertsLayout::Vertical;
40+
}
41+
}
5642
}
5743
});
44+
egui::CentralPanel::default().show(ctx, |ui| match self.app_config.alerts_layout {
45+
AlertsLayout::Vertical => {
46+
ui.with_layout(Layout::top_down(Align::TOP), |ui| {
47+
self.show_alerts(ui);
48+
});
49+
}
50+
AlertsLayout::Horizontal => {
51+
ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
52+
self.show_alerts(ui);
53+
});
54+
}
5855
});
5956
}
57+
58+
fn show_alerts(&mut self, ui: &mut egui::Ui) {
59+
// load warning based on telemetry data
60+
let mut abs_image = egui::include_image!("../../assets/brake-green.png");
61+
let mut shift_image = egui::include_image!("../../assets/shift-orange.png");
62+
let mut wheelspin_image = egui::include_image!("../../assets/wheelspin-green.png");
63+
if let Some(back) = self.telemetry_points.back() {
64+
if back.brake > 0.4 && !back.abs_active {
65+
abs_image = egui::include_image!("../../assets/brake-orange.png");
66+
}
67+
if back.abs_active {
68+
abs_image = egui::include_image!("../../assets/brake-red.png");
69+
}
70+
71+
if back.cur_rpm > back.car_shift_ideal_rpm - 100.
72+
&& back.cur_rpm < back.car_shift_ideal_rpm + 100.
73+
{
74+
shift_image = egui::include_image!("../../assets/shift-green.png");
75+
}
76+
77+
if let Some(TelemetryAnnotation::Bool(true)) = back.annotations.get("wheelspin") {
78+
wheelspin_image = egui::include_image!("../../assets/wheelspin-red.png");
79+
}
80+
}
81+
82+
ui.label("ABS");
83+
ui.add(Image::new(abs_image));
84+
ui.separator();
85+
ui.label("Shift");
86+
ui.add(Image::new(shift_image));
87+
88+
ui.separator();
89+
ui.label("Traction");
90+
ui.add(Image::new(wheelspin_image));
91+
}
6092
}

src/live/config.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use egui::{Pos2, Vec2};
2+
use serde::{Deserialize, Serialize};
3+
4+
use crate::OcypodeError;
5+
6+
use super::{HISTORY_SECONDS, REFRESH_RATE_MS};
7+
8+
const CONFIG_FILE_NAME: &str = "config.json";
9+
10+
#[derive(Serialize, Deserialize, Debug)]
11+
pub(crate) enum AlertsLayout {
12+
Vertical,
13+
Horizontal,
14+
}
15+
16+
impl AlertsLayout {
17+
pub(crate) fn window_size(&self) -> Vec2 {
18+
match self {
19+
Self::Vertical => Vec2::new(100., 500.),
20+
Self::Horizontal => Vec2::new(500., 100.),
21+
}
22+
}
23+
}
24+
25+
#[derive(Serialize, Deserialize, Debug, Clone)]
26+
pub(crate) struct WindowPosition {
27+
pub(crate) x: f32,
28+
pub(crate) y: f32,
29+
}
30+
31+
impl Default for WindowPosition {
32+
fn default() -> Self {
33+
Self { x: 0., y: 0. }
34+
}
35+
}
36+
37+
impl From<WindowPosition> for Pos2 {
38+
fn from(value: WindowPosition) -> Self {
39+
Pos2::new(value.x, value.y)
40+
}
41+
}
42+
43+
impl From<Pos2> for WindowPosition {
44+
fn from(value: Pos2) -> Self {
45+
Self {
46+
x: value.x,
47+
y: value.y,
48+
}
49+
}
50+
}
51+
52+
#[derive(Serialize, Deserialize, Debug)]
53+
pub(crate) struct AppConfig {
54+
pub(crate) refresh_rate_ms: usize,
55+
pub(crate) window_size_s: usize,
56+
pub(crate) show_alerts: bool,
57+
pub(crate) alerts_layout: AlertsLayout,
58+
pub(crate) telemetry_window_position: WindowPosition,
59+
pub(crate) alert_window_position: WindowPosition,
60+
}
61+
62+
impl Default for AppConfig {
63+
fn default() -> Self {
64+
Self {
65+
refresh_rate_ms: REFRESH_RATE_MS,
66+
window_size_s: HISTORY_SECONDS,
67+
show_alerts: false,
68+
alerts_layout: AlertsLayout::Vertical,
69+
telemetry_window_position: WindowPosition::default(),
70+
alert_window_position: WindowPosition::default(),
71+
}
72+
}
73+
}
74+
75+
impl AppConfig {
76+
pub(crate) fn from_local_file() -> Option<Self> {
77+
let config_path = dirs::config_dir()?.join("ocypode").join(CONFIG_FILE_NAME);
78+
79+
if config_path.exists() {
80+
let file = std::fs::File::open(config_path).expect("Could not open config file");
81+
Some(serde_json::from_reader(file).expect("Could not parse config file"))
82+
} else {
83+
None
84+
}
85+
}
86+
87+
pub(crate) fn save(&self) -> Result<(), OcypodeError> {
88+
let config_path = dirs::config_dir()
89+
.ok_or(OcypodeError::NoConfigDir)?
90+
.join("ocypode")
91+
.join(CONFIG_FILE_NAME);
92+
93+
if !config_path.exists() {
94+
std::fs::create_dir_all(config_path.parent().unwrap())
95+
.map_err(|e| OcypodeError::ConfigIOError { source: e })?;
96+
}
97+
98+
let file = std::fs::File::create(config_path)
99+
.map_err(|e| OcypodeError::ConfigIOError { source: e })?;
100+
serde_json::to_writer(file, self)
101+
.map_err(|e| OcypodeError::ConfigSerializeError { source: e })
102+
}
103+
}

0 commit comments

Comments
 (0)