Migration

This commit is contained in:
2024-03-21 09:35:29 +01:00
commit ffc6304f6e
8206 changed files with 26300 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
[package]
name = "launcher_client_egui"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
launcher_shared = { path = "../launcher_shared" }
eframe = "0.21"
egui-winit = "0.21"
reqwest = { version = "0.11", features = ["stream"] }
ehttp = "0.2"
poll-promise = "0.2"
directories = "4.0"

View File

@@ -0,0 +1,120 @@
use eframe::egui;
pub fn custom_window_frame(
ctx: &egui::Context,
frame: &mut eframe::Frame,
title: &str,
add_contents: impl FnOnce(&mut egui::Ui),
) {
use egui::*;
let panel_frame = egui::Frame {
fill: ctx.style().visuals.window_fill(),
rounding: 10.0.into(),
stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke,
outer_margin: 0.5.into(),
..Default::default()
};
CentralPanel::default().frame(panel_frame).show(ctx, |ui| {
let app_rect = ui.max_rect();
let title_bar_height = 32.0;
let title_bar_rect = {
let mut rect = app_rect;
rect.max.y = rect.min.y + title_bar_height;
rect
};
title_bar_ui(ui, frame, title_bar_rect, title);
let content_rect = {
let mut rect = app_rect;
rect.min.y = title_bar_rect.max.y;
rect
}
.shrink(4.0);
let mut content_ui = ui.child_ui(content_rect, *ui.layout());
add_contents(&mut content_ui);
});
}
fn title_bar_ui(
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
title_bar_rect: eframe::epaint::Rect,
title: &str,
) {
use egui::*;
let painter = ui.painter();
let title_bar_response = ui.interact(title_bar_rect, Id::new("title_bar"), Sense::click());
painter.text(
title_bar_rect.center(),
Align2::CENTER_CENTER,
title,
FontId::proportional(20.0),
ui.style().visuals.text_color(),
);
painter.line_segment(
[
title_bar_rect.left_bottom() + vec2(1.0, 0.0),
title_bar_rect.right_bottom() + vec2(-1.0, 0.0),
],
ui.visuals().widgets.noninteractive.bg_stroke,
);
if title_bar_response.double_clicked() {
frame.set_maximized(!frame.info().window_info.maximized);
} else if title_bar_response.is_pointer_button_down_on() {
frame.drag_window();
}
ui.allocate_ui_at_rect(title_bar_rect, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.visuals_mut().button_frame = false;
ui.add_space(8.0);
close_maximise_minimize(ui, frame);
});
});
}
fn close_maximise_minimize(ui: &mut egui::Ui, frame: &mut eframe::Frame) {
use egui::{Button, RichText};
let button_height = 12.0;
let close_response = ui
.add(Button::new(RichText::new("X").size(button_height)))
.on_hover_text("Close the window");
if close_response.clicked() {
frame.close();
}
if frame.info().window_info.maximized {
let maximized_response = ui
.add(Button::new(RichText::new("-").size(button_height)))
.on_hover_text("Restore window");
if maximized_response.clicked() {
frame.set_maximized(false);
}
} else {
let maximized_response = ui
.add(Button::new(RichText::new("-").size(button_height)))
.on_hover_text("Maximize window");
if maximized_response.clicked() {
frame.set_maximized(true);
}
}
let minimized_response = ui
.add(Button::new(RichText::new("_").size(button_height)))
.on_hover_text("Minimize the window");
if minimized_response.clicked() {
frame.set_minimized(true);
}
}

View File

@@ -0,0 +1,54 @@
use std::{fs::File, io::Cursor};
type FetchResult<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#[allow(dead_code)]
async fn fetch_url(url: String, file_name: String) -> FetchResult<()> {
let response = reqwest::get(url).await?;
let mut file = File::create(file_name)?;
let mut content = Cursor::new(response.bytes().await?);
std::io::copy(&mut content, &mut file)?;
Ok(())
}
// use reqwest::{header::CONTENT_LENGTH, Client};
// use std::{path::Path, io::Write};
// #[allow(dead_code)]
// async fn download_file_with_progress(url: &str, file_path: &Path) -> Result<(), reqwest::Error> {
// let client = Client::new();
// let response = client.head(url).send().await?;
// let content_length = response.headers()
// .get(CONTENT_LENGTH)
// .and_then(|v| v.to_str().ok()).and_then(|v| v.parse::<u64>().ok());
// if let Some(content_length) = content_length {
// println!("Downloading {} bytes from {}", content_length, url);
// } else {
// println!("Downloading from {}", url);
// }
// let mut response = client.get(url).send().await?;
// let mut file = File::create(file_path)?;
// let mut bytes_written = 0;
// while let Some(chunk) = response.chunk().await? {
// file.write_all(&chunk)?;
// bytes_written += chunk.len();
// if let Some(content_length) = content_length {
// let progress = (bytes_written as f64 / content_length as f64) * 100.0;
// print!("\rDownloading {:.2}% ({} of {} bytes)", progress, bytes_written, content_length);
// }
// }
// if let Some(content_length) = content_length {
// println!("\rDownload complete ({} bytes)", content_length);
// } else {
// println!("\rDownload complete");
// }
// Ok(())
// }

View File

@@ -0,0 +1,119 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use eframe::{
egui::{widgets, CentralPanel, Context},
App,
};
use poll_promise::Promise;
use crate::custom_window_frame;
pub struct Launcher {
latest_version: String,
update_available: bool,
download_promise: Option<Promise<Result<String, String>>>,
server_url: String,
server_root_uri: String,
dirtreecontent: Option<String>,
}
// {
// "version": "0.1.0",
// "files": [
// {
// "path": "/MyFile",
// "hash": "bighashofdoom"
// }
// ]
// }
impl Launcher {
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
Launcher {
latest_version: "0.1.0".to_string(),
update_available: true,
download_promise: None,
server_url: "https://ginger.amasson.eu:6886".into(),
server_root_uri: format!("/Howling/{}", std::env::consts::OS),
dirtreecontent: None,
}
}
pub fn dirtree_path(&self) -> String {
format!("{}{}/dirtree.json", self.server_url, self.server_root_uri)
}
}
impl App for Launcher {
fn clear_color(&self, _visuals: &eframe::egui::Visuals) -> [f32; 4] {
eframe::egui::Rgba::TRANSPARENT.to_array()
}
fn update(&mut self, ctx: &Context, frame: &mut eframe::Frame) {
let dirtreepath = self.dirtree_path();
custom_window_frame::custom_window_frame(ctx, frame, "egui with custom frame", |ui| {
let promise = self.download_promise.get_or_insert_with(|| {
let ctx = ctx.clone();
let (sender, promise) = Promise::new();
let request = ehttp::Request::get(&dirtreepath);
println!("Downloading from {}", &dirtreepath);
ehttp::fetch(request, move |response| {
let dirtreefile = response.and_then(|res| {
let bytes = res.bytes;
Ok(String::from_utf8(bytes).unwrap())
});
sender.send(dirtreefile);
ctx.request_repaint();
});
promise
});
ui.heading("Howling Launcher");
if self.update_available {
if ui.button("Download Update").clicked() {
println!("download update clicked !");
CentralPanel::default().show(ctx, |ui| match promise.ready() {
None => {
ui.spinner();
println!("Downloading...");
}
Some(Err(err)) => {
println!("Error downloading {}", err);
ui.colored_label(ui.visuals().error_fg_color, err);
}
Some(Ok(text)) => {
println!("Finished downloading");
self.dirtreecontent = Some(text.clone());
}
});
self.update_available = false;
}
} else {
if let Some(text) = self.dirtreecontent.clone() {
ui.label(format!("dirtree:\n{}\n", text));
}
ui.label(format!(
"version: {} - You're up to date !",
self.latest_version
));
}
if ui.button("Play").clicked() {
println!("play clicked !");
}
ui.add_space(ui.available_size_before_wrap().y / 2.0);
ui.horizontal(|ui| {
ui.label("egui theme:");
widgets::global_dark_light_mode_buttons(ui);
});
});
}
}

View File

@@ -0,0 +1,20 @@
mod custom_window_frame;
mod download_file;
mod launcher;
use eframe::{epaint::Vec2, run_native, NativeOptions};
use launcher::Launcher;
fn main() {
_ = run_native(
"Howling Launcher",
NativeOptions {
decorated: false,
transparent: true,
resizable: false,
initial_window_size: Some(Vec2 { x: 500.0, y: 350.0 }),
..Default::default()
},
Box::new(|cc| Box::new(Launcher::new(cc))),
);
}