Compare commits
10 Commits
89e64d3896
...
3fb5042e64
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fb5042e64 | |||
| ee71c28dcc | |||
| 60a5340895 | |||
| f31af10c9f | |||
| c72001a991 | |||
| a119162d28 | |||
| eb6903e8a3 | |||
| 5b81f2bef6 | |||
| cddd54ee33 | |||
| 62e36cd0a2 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -203,7 +203,7 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "practicalrunner"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fontconfig",
|
||||
|
||||
@@ -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"
|
||||
|
||||
35
README.md
35
README.md
@@ -2,8 +2,43 @@
|
||||
|
||||
A practical application runner written in Rust. Inspired by [dmenu](https://tools.suckless.org/dmenu/)
|
||||
|
||||
|
||||
### Screenshots
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
### 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
|
||||
|
||||
BIN
screenshots/screenshot-1716407683.webp
Normal file
BIN
screenshots/screenshot-1716407683.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
screenshots/screenshot-1716407811.webp
Normal file
BIN
screenshots/screenshot-1716407811.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
screenshots/screenshot-1716408039.webp
Normal file
BIN
screenshots/screenshot-1716408039.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user