Code formatted by Zed automatically, added black fill color for

directory button that is currently selected, removed the current
directory text
This commit is contained in:
csd4ni3l
2026-02-05 16:26:44 +01:00
parent abb7704e21
commit 21d9f6c08b

View File

@@ -1,15 +1,12 @@
use bevy::{ use bevy::{log::Level, prelude::*};
log::Level,
prelude::*,
};
use std::{collections::HashMap, fs::File, io::BufReader, path::Path}; use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use bevy_egui::{ use bevy_egui::{EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui};
EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui,
}; use egui::ecolor::Color32;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod linux_lib; mod linux_lib;
@@ -17,7 +14,10 @@ mod linux_lib;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows_lib; mod windows_lib;
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink, Source, cpal::{self, traits::HostTrait}}; use rodio::{
Decoder, OutputStream, OutputStreamBuilder, Sink, Source,
cpal::{self, traits::HostTrait},
};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct JSONData { struct JSONData {
@@ -35,7 +35,7 @@ struct PlayingSound {
struct SoundSystem { struct SoundSystem {
virtual_mic_stream: OutputStream, virtual_mic_stream: OutputStream,
// normal_output_stream: OutputStream, // normal_output_stream: OutputStream,
paused: bool paused: bool,
} }
#[derive(Resource)] #[derive(Resource)]
@@ -47,7 +47,7 @@ struct AppState {
sound_system: SoundSystem, sound_system: SoundSystem,
virt_outputs: Vec<(String, String)>, virt_outputs: Vec<(String, String)>,
virt_output_index_switch: String, virt_output_index_switch: String,
virt_output_index: String virt_output_index: String,
} }
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"]; const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
@@ -59,15 +59,22 @@ fn create_virtual_mic() -> OutputStream {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
return linux_lib::create_virtual_mic_linux(); return linux_lib::create_virtual_mic_linux();
#[allow(unreachable_code)] { #[allow(unreachable_code)]
println!("Unknown/unsupported OS. Audio support may not work or may route to default output (headset, headphones, etc)."); {
println!(
"Unknown/unsupported OS. Audio support may not work or may route to default output (headset, headphones, etc)."
);
let host = cpal::default_host(); let host = cpal::default_host();
let virtual_mic = host.default_output_device().expect("Could not get default output device"); let virtual_mic = host
return OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream") .default_output_device()
.expect("Could not get default output device");
return OutputStreamBuilder::from_device(virtual_mic)
.expect("Unable to open default audio device")
.open_stream()
.expect("Failed to open stream");
// normal_output = host.default_output_device().expect("Could not get default output device"); // normal_output = host.default_output_device().expect("Could not get default output device");
// return (OutputStreamBuilder::from_device(normal_output).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"), OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream")); // return (OutputStreamBuilder::from_device(normal_output).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"), OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"));
} }
} }
fn reload_sound() -> OutputStream { fn reload_sound() -> OutputStream {
@@ -77,7 +84,7 @@ fn reload_sound() -> OutputStream {
return create_virtual_mic(); return create_virtual_mic();
} }
fn list_outputs() -> Vec<(String, String)>{ fn list_outputs() -> Vec<(String, String)> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
return Vec::from([("Select in apps".to_string(), String::from("9999999"))]); return Vec::from([("Select in apps".to_string(), String::from("9999999"))]);
@@ -119,11 +126,11 @@ fn main() {
sound_system: SoundSystem { sound_system: SoundSystem {
virtual_mic_stream, virtual_mic_stream,
// normal_output_stream, // normal_output_stream,
paused: false paused: false,
}, },
virt_outputs: Vec::new(), virt_outputs: Vec::new(),
virt_output_index_switch: String::from("0"), virt_output_index_switch: String::from("0"),
virt_output_index: String::from("999") virt_output_index: String::from("999"),
}) })
.add_systems( .add_systems(
PreStartup, PreStartup,
@@ -179,7 +186,15 @@ fn load_data(app_state: &mut AppState) {
.filter_map(|entry| { .filter_map(|entry| {
entry.ok().and_then(|e| { entry.ok().and_then(|e| {
let path = e.path(); let path = e.path();
if path.is_file() && ALLOWED_FILE_EXTENSIONS.contains(&path.extension().unwrap_or_default().to_str().expect("Could not convert extension to string")) { if path.is_file()
&& ALLOWED_FILE_EXTENSIONS.contains(
&path
.extension()
.unwrap_or_default()
.to_str()
.expect("Could not convert extension to string"),
)
{
path.to_str().map(|s| s.to_string()) path.to_str().map(|s| s.to_string())
} else { } else {
None None
@@ -197,9 +212,7 @@ fn setup_camera_system(mut commands: Commands) {
commands.spawn(Camera2d); commands.spawn(Camera2d);
} }
fn update_ui_scale_factor_system( fn update_ui_scale_factor_system(egui_context: Single<(&mut EguiContextSettings, &Camera)>) {
egui_context: Single<(&mut EguiContextSettings, &Camera)>,
) {
let (mut egui_settings, camera) = egui_context.into_inner(); let (mut egui_settings, camera) = egui_context.into_inner();
egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5); egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5);
} }
@@ -208,7 +221,10 @@ fn play_sound(file_path: String, app_state: &mut AppState) {
let virtual_file = File::open(&file_path).unwrap(); let virtual_file = File::open(&file_path).unwrap();
let virtual_src = Decoder::new(BufReader::new(virtual_file)).unwrap(); let virtual_src = Decoder::new(BufReader::new(virtual_file)).unwrap();
let virtual_sink = Sink::connect_new(&app_state.sound_system.virtual_mic_stream.mixer()); let virtual_sink = Sink::connect_new(&app_state.sound_system.virtual_mic_stream.mixer());
let length = virtual_src.total_duration().expect("Could not get source duration").as_secs_f32(); let length = virtual_src
.total_duration()
.expect("Could not get source duration")
.as_secs_f32();
virtual_sink.append(virtual_src); virtual_sink.append(virtual_src);
virtual_sink.play(); virtual_sink.play();
@@ -244,9 +260,10 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
let mut mic_name = "Select inside apps".to_string(); let mut mic_name = "Select inside apps".to_string();
#[cfg(target_os = "linux")] { #[cfg(target_os = "linux")]
{
let output_index = app_state.virt_output_index.clone(); let output_index = app_state.virt_output_index.clone();
let output_device = linux_lib::get_device_by_index("source-outputs", output_index); let output_device = linux_lib::get_device_by_index("source-outputs", output_index);
if let Some(app_name) = output_device["properties"]["application.name"].as_str() { if let Some(app_name) = output_device["properties"]["application.name"].as_str() {
mic_name = app_name.to_string(); mic_name = app_name.to_string();
} }
@@ -260,9 +277,13 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
.height(available_height / 15.0) .height(available_height / 15.0)
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
for output in &outputs { for output in &outputs {
ui.selectable_value(&mut app_state.virt_output_index_switch, output.1.clone(), output.0.clone()); ui.selectable_value(
&mut app_state.virt_output_index_switch,
output.1.clone(),
output.0.clone(),
);
} }
}); });
if ui if ui
.add_sized( .add_sized(
@@ -327,14 +348,18 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
ui.horizontal(|ui| { ui.horizontal(|ui| {
if app_state.sound_system.paused { if app_state.sound_system.paused {
ui.heading("Paused"); ui.heading("Paused");
} } else {
else {
ui.heading("Playing"); ui.heading("Playing");
} }
ui.vertical(|ui| { ui.vertical(|ui| {
for playing_sound in &app_state.currently_playing { for playing_sound in &app_state.currently_playing {
ui.label(format!("{} - {:.2} / {:.2}", playing_sound.file_path, playing_sound.virtual_sink.get_pos().as_secs_f32(), playing_sound.length)); ui.label(format!(
"{} - {:.2} / {:.2}",
playing_sound.file_path,
playing_sound.virtual_sink.get_pos().as_secs_f32(),
playing_sound.length
));
} }
}) })
}); });
@@ -342,15 +367,22 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
let available_height = ui.available_height(); let available_height = ui.available_height();
ui.horizontal(|ui| { ui.horizontal(|ui| {
let available_width = ui.available_width(); let available_width = ui.available_width();
let current_directories = app_state.loaded_files.keys().cloned().collect::<Vec<_>>(); let current_directories = app_state.loaded_files.keys().cloned().collect::<Vec<_>>();
for directory in current_directories.clone() { for directory in current_directories.clone() {
let mut button = egui::Button::new(&directory);
if directory == app_state.current_directory {
button = button.fill(Color32::BLACK);
}
if ui if ui
.add_sized( .add_sized(
[available_width / current_directories.len() as f32, available_height / 15.0], [
egui::Button::new(&directory), available_width / current_directories.len() as f32,
available_height / 15.0,
],
button,
) )
.clicked() .clicked()
{ {
@@ -359,10 +391,6 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
} }
}); });
ui.add_space(available_height / 50.0); ui.add_space(available_height / 50.0);
ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| {
ui.label(egui::RichText::new(format!("The current directory is {}", app_state.current_directory)).font(egui::FontId::proportional(20.0)));
});
ui.add_space(available_height / 50.0);
if app_state.current_directory.chars().count() > 0 { if app_state.current_directory.chars().count() > 0 {
let files = app_state let files = app_state
.loaded_files .loaded_files
@@ -373,10 +401,13 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
egui::ScrollArea::vertical().show(ui, |ui| { egui::ScrollArea::vertical().show(ui, |ui| {
for element in files { for element in files {
if let Some(filename) = element.split("/").collect::<Vec<_>>().last() { if let Some(filename) = element.split("/").collect::<Vec<_>>().last() {
if ui.add_sized( if ui
[ui.available_width(), available_height / 15.0], .add_sized(
egui::Button::new(*filename), [ui.available_width(), available_height / 15.0],
).clicked() { egui::Button::new(*filename),
)
.clicked()
{
let path = Path::new(&app_state.current_directory) let path = Path::new(&app_state.current_directory)
.join(filename) .join(filename)
.to_string_lossy() .to_string_lossy()