add window icon
This commit is contained in:
parent
d8e6baebf1
commit
841ff739dd
13 changed files with 659 additions and 113 deletions
|
@ -13,8 +13,9 @@ use wgpu_text::glyph_brush::ab_glyph::FontRef;
|
|||
use wgpu_text::glyph_brush::{HorizontalAlign, Layout, OwnedSection, OwnedText, VerticalAlign};
|
||||
use wgpu_text::{BrushBuilder, TextBrush};
|
||||
use winit::window::Window;
|
||||
use crate::error::{ZenyxError, ZenyxErrorKind};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::error::{ZenyxError, ZenyxErrorKind};
|
||||
|
||||
const SHADER_SRC: &str = include_str!("shader.wgsl");
|
||||
|
||||
|
@ -130,7 +131,7 @@ struct Model {
|
|||
bind_group: wgpu::BindGroup,
|
||||
index_count: u32,
|
||||
transform: Matrix4<f32>,
|
||||
version: u32,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
|
@ -174,7 +175,7 @@ impl Model {
|
|||
bind_group,
|
||||
index_count: indices.len() as u32,
|
||||
transform: Matrix4::identity(),
|
||||
version: 1
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +191,6 @@ impl Model {
|
|||
self.version += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct Renderer<'window> {
|
||||
|
@ -211,7 +211,7 @@ pub struct Renderer<'window> {
|
|||
frame_count: u32,
|
||||
fps: f32,
|
||||
font_state: FontState,
|
||||
model_versions: Vec<u32>,
|
||||
model_versions: Vec<u32>,
|
||||
}
|
||||
|
||||
struct FontState {
|
||||
|
@ -370,6 +370,8 @@ impl<'window> Renderer<'window> {
|
|||
surface.configure(&device, &surface_config);
|
||||
let (depth_texture, depth_texture_view) =
|
||||
create_depth_texture(&device, surface_config.width, surface_config.height);
|
||||
let (depth_texture, depth_texture_view) =
|
||||
create_depth_texture(&device, surface_config.width, surface_config.height);
|
||||
|
||||
let font_bytes = include_bytes!("DejaVuSans.ttf");
|
||||
let font = FontRef::try_from_slice(font_bytes).map_err(|e| {
|
||||
|
@ -385,8 +387,6 @@ impl<'window> Renderer<'window> {
|
|||
let scale = base_scale * (surface_config.width as f32 / base_width as f32).clamp(0.5, 2.0);
|
||||
let color = wgpu::Color::WHITE;
|
||||
|
||||
|
||||
|
||||
let section = OwnedSection::default()
|
||||
.add_text(OwnedText::new("FPS: 0.00").with_scale(scale).with_color([
|
||||
color.r as f32,
|
||||
|
@ -430,7 +430,7 @@ impl<'window> Renderer<'window> {
|
|||
scale,
|
||||
color,
|
||||
},
|
||||
model_versions: vec![]
|
||||
model_versions: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -475,16 +475,16 @@ impl<'window> Renderer<'window> {
|
|||
for (i, model) in self.models.iter_mut().enumerate() {
|
||||
let angle = Rad(elapsed * 0.8 + i as f32 * 0.3);
|
||||
if i % 2 == 0 {
|
||||
model.set_transform(Matrix4::from_angle_y(angle));
|
||||
model.set_transform(Matrix4::from_angle_y(angle));
|
||||
} else {
|
||||
model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
|
||||
model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
|
||||
}
|
||||
}
|
||||
for (i, model) in self.models.iter().enumerate() {
|
||||
if model.version > self.model_versions[i] {
|
||||
model.update(&self.queue);
|
||||
#[cfg(debug_assertions)]
|
||||
trace!("Updating model: {:#?}",model);
|
||||
trace!("Updating model: {:#?}", model);
|
||||
self.model_versions[i] = model.version;
|
||||
}
|
||||
}
|
||||
|
@ -604,9 +604,11 @@ impl<'window> Renderer<'window> {
|
|||
pub fn bg_color(&self) -> &wgpu::Color {
|
||||
&self.bg_color
|
||||
}
|
||||
|
||||
pub fn text_color(&self) -> &wgpu::Color {
|
||||
&self.font_state.color
|
||||
}
|
||||
|
||||
pub fn set_text_color(&mut self, color: wgpu::Color) {
|
||||
self.font_state.color = color;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ctx::{Renderer, Vertex};
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::dpi::Size;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use image::ImageDecoder;
|
||||
use image::ImageFormat;
|
||||
use tobj::Mesh;
|
||||
use tobj::{LoadOptions, Model};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use wgpu::rwh::HasWindowHandle;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::dpi::Size;
|
||||
use winit::event::{KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::monitor::MonitorHandle;
|
||||
use winit::platform::windows::WindowAttributesExtWindows;
|
||||
use winit::window::Fullscreen;
|
||||
use winit::window::Icon;
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowId;
|
||||
|
||||
|
@ -90,7 +95,14 @@ f 6/11/6 5/10/6 1/1/6 2/13/6
|
|||
|
||||
impl App<'_> {
|
||||
fn create_main_window(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let win_attr = Window::default_attributes().with_title("Zenyx").with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)));
|
||||
let icon = self.load_icon_from_bytes(Self::ICON).unwrap();
|
||||
|
||||
let win_attr = Window::default_attributes()
|
||||
.with_title("Zenyx")
|
||||
.with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)))
|
||||
.with_window_icon(icon.clone())
|
||||
.with_taskbar_icon(icon);
|
||||
|
||||
match event_loop.create_window(win_attr) {
|
||||
Ok(window) => {
|
||||
let window = Arc::new(window);
|
||||
|
@ -160,7 +172,7 @@ impl App<'_> {
|
|||
) {
|
||||
if !key_event.state.is_pressed() || key_event.repeat {
|
||||
return;
|
||||
}
|
||||
}
|
||||
match key_event.physical_key {
|
||||
winit::keyboard::PhysicalKey::Code(code) => match code {
|
||||
winit::keyboard::KeyCode::Space => {
|
||||
|
@ -199,6 +211,7 @@ impl App<'_> {
|
|||
warn!("No window context for toggling background: {:?}", window_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_fullscreen(&mut self, window_id: WindowId) {
|
||||
if let Some(ctx) = self.windows.get_mut(&window_id) {
|
||||
let is_fullscreen = ctx.window.fullscreen().is_some();
|
||||
|
@ -216,13 +229,43 @@ impl App<'_> {
|
|||
warn!("No window found for fullscreen toggle: {:?}", window_id);
|
||||
}
|
||||
}
|
||||
fn load_icon_from_bytes(&self, bytes: &[u8]) -> Result<Option<Icon>, String> {
|
||||
const IMAGE_DIR: &str = env!("CARGO_MANIFEST_DIR");
|
||||
let cursor = Cursor::new(bytes);
|
||||
let format = image::guess_format(bytes).map_err(|_| "Failed to guess image format")?;
|
||||
let decoder = match format {
|
||||
ImageFormat::Png => image::codecs::png::PngDecoder::new(cursor).map_err(|e| format!("Failed to decode PNG: {}", e))?,
|
||||
_ => {
|
||||
let img = image::load_from_memory(bytes).map_err(|e| format!("Failed to load image: {}", e))?.into_rgba8();
|
||||
let (width, height) = img.dimensions();
|
||||
return Icon::from_rgba(img.into_raw(), width, height)
|
||||
.map(Some)
|
||||
.map_err(|e| format!("Failed to create icon from bytes: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
let (width, height) = decoder.dimensions();
|
||||
let mut image_data = vec![0; decoder.total_bytes() as usize];
|
||||
decoder.read_image(&mut image_data).map_err(|e| format!("Failed to read image data: {}", e))?;
|
||||
|
||||
Icon::from_rgba(image_data, width, height)
|
||||
.map(Some)
|
||||
.map_err(|e| format!("Failed to create icon from bytes: {}", e))
|
||||
}
|
||||
|
||||
const ICON: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/Badge.png"));
|
||||
fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) {
|
||||
let title = format!("Zenyx - New Window {}", self.windows.len());
|
||||
// TODO: Verify that this is safe instead of matching on it
|
||||
let icon = self.load_icon_from_bytes(Self::ICON).unwrap();
|
||||
|
||||
let win_attr = unsafe {
|
||||
let base = Window::default_attributes().with_title(title).with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)));
|
||||
let base = Window::default_attributes()
|
||||
.with_title(title)
|
||||
.with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)))
|
||||
.with_window_icon(icon.clone())
|
||||
.with_taskbar_icon(icon);
|
||||
|
||||
match main_ctx.window_handle() {
|
||||
Ok(handle) => {
|
||||
if !cfg!(target_os = "windows") {
|
||||
|
@ -230,11 +273,11 @@ impl App<'_> {
|
|||
} else {
|
||||
base
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
base
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
match event_loop.create_window(win_attr) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue