diff --git a/inlyne.default.toml b/inlyne.default.toml index 0fe7949a..a8224366 100644 --- a/inlyne.default.toml +++ b/inlyne.default.toml @@ -20,6 +20,16 @@ theme = "Auto" # lines-to-scroll = 4.5 lines-to-scroll = 3.0 +# Position of the window +# [position] +# x = 500 +# y = 200 + +# Geomertry/Size of the window in pixels +# [size] +# width = 600 +# height = 500 + # The light and dark themes can be customized as well # Both the light and dark theme colors can be fully customized [dark-theme] diff --git a/src/main.rs b/src/main.rs index f5902dc9..120dbc73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ use winit::event::{ ElementState, Event, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, WindowEvent, }; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; -use winit::window::{CursorIcon, Window}; +use winit::window::{CursorIcon, Window, WindowBuilder}; pub enum InlyneEvent { LoadedImage(String, Arc>>), @@ -156,8 +156,20 @@ impl Inlyne { let file_path = opts.history.get_path().to_owned(); let event_loop = EventLoopBuilder::::with_user_event().build(); - let window = Arc::new(Window::new(&event_loop).unwrap()); - window.set_title(&utils::format_title(&file_path)); + + let window = { + let mut wb = WindowBuilder::new().with_title(&utils::format_title(&file_path)); + + if let Some(ref pos) = opts.position { + wb = wb.with_position(winit::dpi::PhysicalPosition::new(pos.x, pos.y)) + } + if let Some(ref size) = opts.size { + wb = wb.with_inner_size(winit::dpi::PhysicalSize::new(size.width, size.height)) + } + + Arc::new(wb.build(&event_loop).unwrap()) + }; + let renderer = pollster::block_on(Renderer::new( &window, opts.theme.clone(), diff --git a/src/opts/cli.rs b/src/opts/cli.rs index fb1d6724..1babc300 100644 --- a/src/opts/cli.rs +++ b/src/opts/cli.rs @@ -1,3 +1,4 @@ +use super::{Position, Size}; use clap::{ builder::PossibleValue, command, value_parser, Args as ClapArgs, Parser, Subcommand, ValueEnum, }; @@ -68,7 +69,7 @@ pub enum Commands { Config(ConfigCmd), } -/// View markdown a file with inlyne +/// View a markdown file with inlyne #[derive(ClapArgs, PartialEq, Debug, Clone, Default)] #[command(arg_required_else_help(true))] pub struct View { @@ -91,6 +92,14 @@ pub struct View { /// Maximum width of page in pixels #[arg(short = 'w', long = "page-width")] pub page_width: Option, + + /// Position of the opened window , + #[arg(short = 'p', long = "win-pos", value_parser = value_parser!(Position))] + pub position: Option, + + /// Size of the opened window x + #[arg(short = 'g', long = "win-size", value_parser = value_parser!(Size))] + pub size: Option, } /// Configuration related things diff --git a/src/opts/config.rs b/src/opts/config.rs index 4de7626e..86ac5c84 100644 --- a/src/opts/config.rs +++ b/src/opts/config.rs @@ -1,6 +1,7 @@ use std::fs::{create_dir_all, read_to_string}; use std::io::Write; use std::path::{Path, PathBuf}; +use std::str::FromStr; use super::ThemeType; use crate::color; @@ -88,6 +89,53 @@ pub struct DebugSection { pub metrics: Option, } +#[derive(Deserialize, Clone, Debug, Default, PartialEq)] +pub struct Position { + pub x: i32, + pub y: i32, +} + +impl FromStr for Position { + type Err = &'static str; + + fn from_str(input: &str) -> Result { + let parts: Vec<&str> = input.split(',').collect(); + if parts.len() != 2 { + return Err("Invalid format for Position: expected format ,"); + } + let x = parts[0] + .parse::() + .map_err(|_| "Invalid x-coordinate: not a valid integer")?; + let y = parts[1] + .parse::() + .map_err(|_| "Invalid y-coordinate: not a valid integer")?; + Ok(Position { x, y }) + } +} + +#[derive(Deserialize, Clone, Debug, Default, PartialEq)] +pub struct Size { + pub width: u32, + pub height: u32, +} +impl FromStr for Size { + type Err = &'static str; + + fn from_str(input: &str) -> Result { + let parts: Vec<&str> = input.split('x').collect(); + if parts.len() != 2 { + return Err("Invalid format for Size: expected format x"); + } + let width = parts[0] + .parse::() + .map_err(|_| "Invalid width: not a valid integer")?; + let height = parts[1] + .parse::() + .map_err(|_| "Invalid height: not a valid integer")?; + Ok(Size { width, height }) + } +} + #[derive(Deserialize, Debug, Default, PartialEq)] #[serde(default, rename_all = "kebab-case")] pub struct Config { @@ -100,6 +148,8 @@ pub struct Config { pub font_options: Option, pub keybindings: KeybindingsSection, pub debug: DebugSection, + pub position: Option, + pub size: Option, } impl Config { diff --git a/src/opts/mod.rs b/src/opts/mod.rs index 705f4cb5..9ae92606 100644 --- a/src/opts/mod.rs +++ b/src/opts/mod.rs @@ -7,7 +7,9 @@ use std::path::Path; use crate::color; pub use cli::{Cli, Commands, ConfigCmd, ThemeType, View}; -pub use config::{Config, DebugSection, FontOptions, KeybindingsSection, MetricsExporter}; +pub use config::{ + Config, DebugSection, FontOptions, KeybindingsSection, MetricsExporter, Position, Size, +}; use crate::history::History; use anyhow::Result; @@ -52,6 +54,8 @@ pub struct Opts { pub keybindings: KeybindingsSection, pub color_scheme: Option, pub metrics: Option, + pub position: Option, + pub size: Option, } impl Opts { @@ -93,6 +97,8 @@ impl Opts { font_options, keybindings, debug, + size, + position, } = config; let View { @@ -101,6 +107,8 @@ impl Opts { scale: args_scale, config: _, page_width: args_page_width, + size: v_size, + position: v_position, } = args; let DebugSection { metrics } = debug; @@ -126,6 +134,9 @@ impl Opts { let page_width = args_page_width.or(config_page_width); let lines_to_scroll = lines_to_scroll.into(); + let position = v_position.or(position); + let size = v_size.or(size); + Ok(Self { history: History::new(file_path), theme, @@ -136,6 +147,8 @@ impl Opts { keybindings, color_scheme: resolved_theme, metrics, + position, + size, }) }