Compare commits

...

10 Commits

7 changed files with 122 additions and 30 deletions

2
Cargo.lock generated
View File

@@ -203,7 +203,7 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "practicalrunner"
version = "0.1.0"
version = "0.1.2"
dependencies = [
"clap",
"fontconfig",

View File

@@ -1,9 +1,13 @@
[package]
name = "practicalrunner"
description = "A practical application runner"
version = "0.1.0"
version = "0.1.2"
authors = ["409"]
edition = "2021"
license = "MIT"
homepage = "https://github.com/4-0-9/practical-runner"
repository = "https://github.com/4-0-9/practical-runner"
readme = "README.md"
[profile.release]
opt-level = "z"

View File

@@ -2,8 +2,43 @@
A practical application runner written in Rust. Inspired by [dmenu](https://tools.suckless.org/dmenu/)
### Screenshots
![Screenshot 1](screenshots/screenshot-1716407683.webp)
![Screenshot 2](screenshots/screenshot-1716407811.webp)
![Screenshot 3](screenshots/screenshot-1716408039.webp)
### Features
- theming (colors, font family, font size, line spacing, window border)
- custom row count
- smart row scrolling
- prompt message
- open menu on a specific display
### Installation
To use practical-runner run the following command:
```console
cargo install practicalrunner
```
Or clone this repository and run the following command in the repository's root directory:
```console
cargo install --path .
```
### Usage
```console
practicalrunner
```
For information about the various arguments run the following command:
```console
practicalrunner --help
```
### Controls
- [enter] to run the selected suggestion
- [up] / [down] arrow to scroll the suggestions
- [escape] / [ctrl-c] to quit

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -8,7 +8,7 @@ use sdl2::{
render::{Canvas, WindowCanvas},
ttf,
video::Window,
Sdl,
Sdl, VideoSubsystem,
};
use crate::{
@@ -26,6 +26,7 @@ pub struct Runner {
input: String,
window_size: (u32, u32),
settings: RunnerMenuSettings,
target_display_index: Option<i32>,
}
impl Runner {
@@ -59,43 +60,43 @@ impl Runner {
let video = context.video().expect("Error initializing SDL video");
let (mut window_x, mut window_y): (i32, i32) = (0, 0);
match settings.display_index {
Some(display) => {
if (display as i32).lt(&video
.num_video_displays()
.expect("Error getting number of displays"))
{
let bounds = video
.display_bounds(display.into())
.expect(&format!("Error getting bounds for display {}", display));
window_x = bounds.x() + (bounds.width().div_euclid(2) as i32)
- window_width.div_euclid(2) as i32;
window_y = bounds.y() + (bounds.height().div_euclid(2) as i32)
- window_height.div_euclid(2) as i32;
}
}
None => {}
}
let window = video
.window("Practical runner", window_width, window_height)
.borderless()
.always_on_top()
.set_shaped()
.build()
.expect("Error creating window");
let mut canvas = window.into_canvas().build().expect("Error creating canvas");
canvas.present();
if window_x.ne(&0) || window_y.ne(&0) {
canvas.window_mut().set_position(
sdl2::video::WindowPos::Positioned(window_x),
sdl2::video::WindowPos::Positioned(window_y),
);
// If we don't call this before window.display_index() it always returns 0
let _ = context
.event_pump()
.expect("Error getting SDL event pump")
.poll_iter()
.count();
let target_display_index: Option<i32>;
let current_display_index = canvas
.window()
.display_index()
.expect("Error getting window display index");
match settings.display_index {
Some(display_index) => {
target_display_index = Some(current_display_index);
center_on_display(canvas.window_mut(), display_index.into(), &video);
}
None => {
target_display_index = None;
center_on_display(canvas.window_mut(), current_display_index.into(), &video);
}
}
canvas.window_mut().raise();
let mut cloned_executables = executables.clone();
@@ -111,6 +112,7 @@ impl Runner {
window_size: (window_width, window_height),
settings,
font_path,
target_display_index,
}
}
@@ -138,6 +140,11 @@ impl Runner {
let mut event_pump = self.context.event_pump().unwrap();
'run: loop {
if !self.canvas.window().has_input_focus() {
self.input.clear();
break 'run;
}
self.canvas.set_draw_color(background_color);
self.canvas.clear();
self.canvas.set_draw_color(border_color);
@@ -329,12 +336,33 @@ impl Runner {
self.canvas.present();
std::thread::sleep(Duration::from_millis(8))
std::thread::sleep(Duration::from_millis(8));
}
if self.input.is_empty() {
None
} else {
match self.target_display_index {
Some(target_display_index) => {
let window = self.canvas.window_mut();
let target_display_bounds = self
.context
.video()
.expect("Error getting SDL video")
.display_bounds(target_display_index)
.expect("Error getting target display bounds");
window.set_position(
sdl2::video::WindowPos::Positioned(target_display_bounds.x()),
sdl2::video::WindowPos::Positioned(target_display_bounds.y()),
);
window.raise();
window.hide();
}
None => (),
}
Some(self.input.clone())
}
}
@@ -373,3 +401,28 @@ fn draw_borders(border_size: u8, window_size: (u32, u32), canvas: &mut WindowCan
let _ = canvas.fill_rect(Rect::new(0, 0, border_size.into(), window_size.1));
}
}
fn center_on_display(window: &mut Window, display_index: i32, video: &VideoSubsystem) {
if (display_index as i32).lt(&video
.num_video_displays()
.expect("Error getting number of displays"))
{
let bounds = video.display_bounds(display_index.into()).expect(&format!(
"Error getting bounds for display {}",
display_index
));
let window_size = window.size();
window.set_position(
sdl2::video::WindowPos::Positioned(
bounds.x() + bounds.width().div_euclid(2) as i32
- window_size.0.div_euclid(2) as i32,
),
sdl2::video::WindowPos::Positioned(
bounds.y() + bounds.height().div_euclid(2) as i32
- window_size.1.div_euclid(2) as i32,
),
);
}
}