commit so that i dont have to use any code blocks :)

This commit is contained in:
RabbyDevs 2025-04-17 19:35:45 +03:00
parent bd72d0a037
commit 574340749b
4 changed files with 177 additions and 115 deletions

View file

@ -56,6 +56,7 @@ impl Frame {
#[derive(Debug)]
pub(crate) struct Internal {
// pub(crate) uri: url::Url,
pub(crate) id: u64,
pub(crate) bus: gst::Bus,
@ -194,6 +195,12 @@ impl Internal {
#[derive(Debug)]
pub struct Video(pub(crate) RwLock<Internal>);
// impl Clone for Video {
// fn clone(&self) -> Self {
// Video::new(self.read().uri.clone()).unwrap()
// }
// }
impl Drop for Video {
fn drop(&mut self) {
let inner = self.0.get_mut().expect("failed to lock");
@ -240,7 +247,12 @@ impl Video {
let text_sink: gst::Element = pipeline.property("text-sink");
let text_sink = text_sink.downcast::<gst_app::AppSink>().unwrap();
Self::from_gst_pipeline(pipeline, video_sink, Some(text_sink))
Self::from_gst_pipeline(
// uri,
pipeline,
video_sink,
Some(text_sink)
)
}
/// Creates a new video based on an existing GStreamer pipeline and appsink.
@ -252,6 +264,7 @@ impl Video {
/// **Note:** Many functions of [`Video`] assume a `playbin` pipeline.
/// Non-`playbin` pipelines given here may not have full functionality.
pub fn from_gst_pipeline(
// uri: url::Url,
pipeline: gst::Pipeline,
video_sink: gst_app::AppSink,
text_sink: Option<gst_app::AppSink>,
@ -414,6 +427,7 @@ impl Video {
});
Ok(Video(RwLock::new(Internal {
// uri,
id,
bus: pipeline.bus().unwrap(),

View file

@ -1,15 +1,19 @@
// #![feature(let_chains)]
use file_format::FileFormat;
use ::image::ImageReader;
use iced::{
alignment::Vertical::Top, border, gradient, mouse, widget::{button, center, column, container, image, mouse_area, row, stack, text, Column, Space}, window::{self, icon, settings::PlatformSpecific, Settings}, Alignment::Center, Color, Element, Length, Renderer, Size, Task, Theme
alignment::Vertical::Top, border, gradient, mouse, widget::{button, center, column, container, image, mouse_area, row, stack, text, Column, Space}, window::{self, icon, Settings}, Alignment::Center, Color, Element, Font, Length, Point, Renderer, Size, Subscription, Task, Theme
};
use iced_video_player::{Video, VideoPlayer};
use serde::{Deserialize, Serialize};
use utils::img_utils::round_image;
use std::{
fs::{self, create_dir_all, read_to_string}, io::{Cursor, Write}, path::PathBuf, thread::sleep
env, fs::{self, create_dir_all, read_to_string}, io::{Cursor, Read, Write}, path::PathBuf, sync::Arc
};
mod utils;
use tempfile::NamedTempFile;
use iced::event::{self, Event};
use iced::keyboard::Event as KeyboardEvent;
use iced::mouse::Event as MouseEvent;
#[derive(rust_embed::Embed)]
#[folder = "resources"]
@ -22,7 +26,7 @@ pub fn main() -> iced::Result {
.unwrap()
.decode()
.unwrap();
let rgba_vec = icon_image.as_rgba8().unwrap().clone().into_vec();
let rgba_vec = icon_image.as_rgba8().unwrap().to_vec();
let settings = Settings {
decorations: false,
@ -68,7 +72,7 @@ struct State {
selected_game: PossibleGames,
installed_games: Vec<PossibleGames>,
installed_game_servers: Vec<PossibleGames>,
db_software_installed: bool
db_software_installed: bool,
}
#[derive(Debug, Default, Serialize, Deserialize)]
@ -93,62 +97,54 @@ enum SaveError {
#[derive(Debug, Clone)]
enum Message {
Loaded(Result<State, LoadError>),
HoverEnter(),
GameSelected(PossibleGames),
DragStarted,
GameSelected(PossibleGames)
}
impl State {
fn path() -> PathBuf {
let mut path = if let Some(project_dirs) =
directories::ProjectDirs::from("rs", "reversed-rooms", "launcher")
{
project_dirs.data_dir().into()
} else {
std::env::current_dir().unwrap_or_default()
};
// fn path() -> PathBuf {
// path.push("launcher-state.json");
path.push("launcher-state.json");
// path
// }
path
}
// fn load() -> Result<State, LoadError> {
// let contents = read_to_string(Self::path()).map_err(|_| LoadError::File)?;
fn load() -> Result<State, LoadError> {
let contents = read_to_string(Self::path()).map_err(|_| LoadError::File)?;
// let saved_state: SavedState =
// serde_json::from_str(&contents).map_err(|_| LoadError::Format)?;
let saved_state: SavedState =
serde_json::from_str(&contents).map_err(|_| LoadError::Format)?;
// Ok(State {
// selected_game: PossibleGames::WutheringWaves,
// installed_games: saved_state.installed_games,
// installed_game_servers: saved_state.installed_game_servers,
// db_software_installed: saved_state.db_software_installed,
// })
// }
Ok(State {
selected_game: PossibleGames::WutheringWaves,
installed_games: saved_state.installed_games,
installed_game_servers: saved_state.installed_game_servers,
db_software_installed: saved_state.db_software_installed,
})
}
// async fn save(self) -> Result<(), SaveError> {
// let saved_state = SavedState {
// installed_games: self.installed_games,
// installed_game_servers: self.installed_game_servers,
// db_software_installed: self.db_software_installed,
// };
async fn save(self) -> Result<(), SaveError> {
let saved_state = SavedState {
installed_games: self.installed_games,
installed_game_servers: self.installed_game_servers,
db_software_installed: self.db_software_installed,
};
// let json = serde_json::to_string_pretty(&saved_state).map_err(|_| SaveError::Format)?;
let json = serde_json::to_string_pretty(&saved_state).map_err(|_| SaveError::Format)?;
// let path = Self::path();
let path = Self::path();
// if let Some(dir) = path.parent() {
// create_dir_all(dir).map_err(|_| SaveError::Write)?;
// }
if let Some(dir) = path.parent() {
create_dir_all(dir).map_err(|_| SaveError::Write)?;
}
// {
// fs::write(path, json.as_bytes()).map_err(|_| SaveError::Write)?;
// }
{
fs::write(path, json.as_bytes()).map_err(|_| SaveError::Write)?;
}
// sleep(std::time::Duration::from_secs(2));
sleep(std::time::Duration::from_secs(2));
Ok(())
}
// Ok(())
// }
}
fn rad(deg: f32) -> f32 {
@ -164,10 +160,11 @@ fn get_game_background(game: &PossibleGames) -> Element<Message> {
};
if let Some(file) = Assets::get(file_path) {
let file_format = FileFormat::from_bytes(file.data.clone());
let data = Arc::new(file.data);
let file_format = FileFormat::from_bytes(&*data);
if file_format.extension() == "mp4" {
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(&file.data.clone()).unwrap();
temp_file.write_all(&data).unwrap();
let temp_path = temp_file.path().to_str().unwrap().to_string();
match Video::new(url::Url::from_file_path(temp_path).unwrap()) {
@ -181,7 +178,7 @@ fn get_game_background(game: &PossibleGames) -> Element<Message> {
},
}
} else {
let img = ImageReader::new(Cursor::new(&file.data))
let img = ImageReader::new(Cursor::new(&*data))
.with_guessed_format()
.unwrap()
.decode()
@ -207,10 +204,8 @@ fn get_game_icon(game: &PossibleGames) -> Element<Message> {
PossibleGames::GenshinImpact => "genshinimpact-icon.png",
};
if let Some(img_file) = Assets::get(file_path) {
let img = ImageReader::new(Cursor::new(img_file.data))
.with_guessed_format()
.unwrap()
.decode()
let data_cursor = Cursor::new(img_file.data);
let img = round_image(data_cursor)
.unwrap()
.resize(126, 126, ::image::imageops::FilterType::Lanczos3);
let handle = image::Handle::from_rgba(
@ -221,8 +216,6 @@ fn get_game_icon(game: &PossibleGames) -> Element<Message> {
container(image(handle).content_fit(iced::ContentFit::Contain).height(Length::Fixed(64.0)).filter_method(image::FilterMethod::Linear))
.style(move |_| {
container::Style {
// text_color: Color::from_rgba8(0, 0, 0, 1.0).into(),
// background: Some(Color::from_rgba8(255, 255, 255, 1.0).into()),
border: border::rounded(20),
..container::Style::default()
}
@ -233,16 +226,16 @@ fn get_game_icon(game: &PossibleGames) -> Element<Message> {
}
}
fn style_container(direction: f32) -> container::Style {
fn style_container(direction: f32, use_gradient: bool) -> container::Style {
let angle = rad(direction);
let gradient: Option<iced::Background> = if use_gradient {
Some(gradient::Linear::new(angle)
.add_stop(0.0, Color::from_rgba8(0, 0, 0, 0.0))
.add_stop(1.0, Color::from_rgba8(0, 0, 0, 0.8)).into())
} else {None};
container::Style {
text_color: Color::from_rgba8(255, 255, 255, 1.0).into(),
background: Some(
gradient::Linear::new(angle)
.add_stop(0.0, Color::from_rgba8(0, 0, 0, 0.0))
.add_stop(1.0, Color::from_rgba8(0, 0, 0, 0.8))
.into(),
),
background: gradient,
..container::Style::default()
}
}
@ -262,12 +255,18 @@ impl Launcher {
*self = Launcher::Loaded(state);
Task::none()
},
Message::HoverEnter() => {
Task::none()
}
_ => Task::none(),
},
_ => Task::none(),
Launcher::Loaded(_) => {
match message {
Message::DragStarted => {
window::get_latest().and_then(move |id: window::Id| {
window::drag(id)
})
},
_ => Task::none()
}
}
}
}
@ -278,65 +277,55 @@ impl Launcher {
get_game_icon(&PossibleGames::ZenlessZoneZero),
get_game_icon(&PossibleGames::HonkaiStarRail),
get_game_icon(&PossibleGames::GenshinImpact),
// text("test").size(25),
// text("test").size(25),
]
.spacing(10),
)
// .padding(10)
.padding(10)
.align_y(Top)
.align_x(Center)
.width(Length::Fill);
// let decorations = container(row![
// button(content)
// ]);
let topbar = container(row![
text("Reversed Rooms").size(25),
Space::new(Length::Fill, Length::Fixed(0.0)),
game_selector,
Space::new(Length::Fill, Length::Fixed(0.0)),
// text("rabbydevs").size(25),
])
.height(Length::Fill)
.width(Length::Fill)
.style(move |_| style_container(0.0))
.padding(10);
let bottom_bar = container(row![
text("insert game announcements").size(25),
Space::new(Length::Fill, Length::Fixed(0.0)),
container(mouse_area(button(text("Launch").size(25))
.padding(10)
.style(move |_, _| {
button::Style {
text_color: Color::from_rgba8(0, 0, 0, 1.0),
background: Some(Color::from_rgba8(255, 255, 255, 1.0).into()),
border: border::rounded(5),
..button::Style::default()
}
})).interaction(iced::mouse::Interaction::Pointer))
])
.width(Length::Fill)
.style(move |_theme| style_container(180.0))
.padding(20);
let user_area: Column<Message, Theme, Renderer> =
column![topbar, Space::new(Length::Fill, Length::Fill), bottom_bar].width(Length::Fill);
let content = container(user_area).center(Length::Fill);
.style(move |_| style_container(0.0, true));
println!("whuh");
match self {
Launcher::Loading => loading_message(),
Launcher::Loading => center(text("Loading...").size(50)).into(),
Launcher::Loaded(state) => {
stack![get_game_background(&state.selected_game), content].into()
let topbar = container(
mouse_area(row![
text("Reversed Rooms").size(25),
Space::new(Length::Fill, Length::Fixed(0.0)),
])
.on_press(Message::DragStarted))
.width(Length::Fill)
.style(move |_| style_container(0.0, false))
.padding(10);
let bottom_bar = container(row![
text("insert game announcements").size(25),
Space::new(Length::Fill, Length::Fixed(0.0)),
container(mouse_area(button(text("Launch").size(25))
.padding(10)
.style(move |_, _| {
button::Style {
text_color: Color::from_rgba8(0, 0, 0, 1.0),
background: Some(Color::from_rgba8(255, 255, 255, 1.0).into()),
border: border::rounded(5),
..button::Style::default()
}
})).interaction(iced::mouse::Interaction::Pointer))
])
.width(Length::Fill)
.style(move |_theme| style_container(180.0, true))
.padding(20);
let user_area: Column<Message, Theme, Renderer> =
column![topbar, Space::new(Length::Fill, Length::Fill), bottom_bar].width(Length::Fill);
let content = container(user_area).center(Length::Fill);
stack![get_game_background(&state.selected_game), game_selector, content].into()
}
}
}
}
fn loading_message<'a>() -> Element<'a, Message> {
center(text("Loading...").size(50)).into()
}

58
src/utils/img_utils.rs Normal file
View file

@ -0,0 +1,58 @@
use std::io::Cursor;
use image::{DynamicImage, GenericImageView, ImageBuffer, ImageReader, Rgba};
pub fn is_in_rounded_rect(x: u32, y: u32, width: u32, height: u32, radius: f32) -> bool {
let x = x as f32;
let y = y as f32;
let width = width as f32;
let height = height as f32;
if x < radius && y < radius {
let dx = x - radius;
let dy = y - radius;
let distance = (dx.powi(2) + dy.powi(2)).sqrt();
return distance <= radius;
} else if x > width - radius && y < radius {
let dx = x - (width - radius);
let dy = y - radius;
let distance = (dx.powi(2) + dy.powi(2)).sqrt();
return distance <= radius;
} else if x < radius && y > height - radius {
let dx = x - radius;
let dy = y - (height - radius);
let distance = (dx.powi(2) + dy.powi(2)).sqrt();
return distance <= radius;
} else if x > width - radius && y > height - radius {
let dx = x - (width - radius);
let dy = y - (height - radius);
let distance = (dx.powi(2) + dy.powi(2)).sqrt();
return distance <= radius;
}
x >= 0.0 && x < width && y >= 0.0 && y < height
}
pub fn round_image(img_data: Cursor<std::borrow::Cow<'static, [u8]>>) -> Result<DynamicImage, Box<dyn std::error::Error>> {
let img = ImageReader::new(img_data)
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
let (width, height) = img.dimensions();
let mut rounded_img = ImageBuffer::new(width, height);
let radius = 50.0;
for (x, y, pixel) in img.pixels() {
if is_in_rounded_rect(x, y, width, height, radius) {
rounded_img.put_pixel(x, y, Rgba(pixel.0));
} else {
rounded_img.put_pixel(x, y, Rgba([0, 0, 0, 0]));
}
}
Ok(DynamicImage::ImageRgba8(rounded_img))
}

1
src/utils/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod img_utils;