mirror of
https://github.com/imgurbot12/rmenu.git
synced 2025-02-04 17:25:00 +01:00
feat: submenu, tranparency-support, better cfgs
This commit is contained in:
parent
e3598ebf2e
commit
7b2741c4c1
9 changed files with 199 additions and 76 deletions
|
@ -10,6 +10,8 @@ askama = "0.12.1"
|
||||||
base64 = "0.21.5"
|
base64 = "0.21.5"
|
||||||
clap = { version = "4.4.11", features = ["derive"] }
|
clap = { version = "4.4.11", features = ["derive"] }
|
||||||
env_logger = "0.10.1"
|
env_logger = "0.10.1"
|
||||||
|
gdk-sys = "0.18.0"
|
||||||
|
gtk-sys = "0.18.0"
|
||||||
heck = "0.4.1"
|
heck = "0.4.1"
|
||||||
keyboard-types = "0.7.0"
|
keyboard-types = "0.7.0"
|
||||||
lastlog = { version = "0.2.3", features = ["libc"] }
|
lastlog = { version = "0.2.3", features = ["libc"] }
|
||||||
|
|
|
@ -7,6 +7,16 @@ use keyboard_types::{Code, Modifiers};
|
||||||
use rmenu_plugin::Options;
|
use rmenu_plugin::Options;
|
||||||
use serde::{de::Error, Deserialize};
|
use serde::{de::Error, Deserialize};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _false() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
// parse supported modifiers from string
|
// parse supported modifiers from string
|
||||||
fn mod_from_str(s: &str) -> Option<Modifiers> {
|
fn mod_from_str(s: &str) -> Option<Modifiers> {
|
||||||
match s.to_lowercase().as_str() {
|
match s.to_lowercase().as_str() {
|
||||||
|
@ -95,8 +105,8 @@ impl Default for KeyConfig {
|
||||||
exit: vec![Keybind::new(Code::Escape)],
|
exit: vec![Keybind::new(Code::Escape)],
|
||||||
move_next: vec![Keybind::new(Code::ArrowDown)],
|
move_next: vec![Keybind::new(Code::ArrowDown)],
|
||||||
move_prev: vec![Keybind::new(Code::ArrowUp)],
|
move_prev: vec![Keybind::new(Code::ArrowUp)],
|
||||||
open_menu: vec![],
|
open_menu: vec![Keybind::new(Code::ArrowRight)],
|
||||||
close_menu: vec![],
|
close_menu: vec![Keybind::new(Code::ArrowLeft)],
|
||||||
jump_next: vec![Keybind::new(Code::PageDown)],
|
jump_next: vec![Keybind::new(Code::PageDown)],
|
||||||
jump_prev: vec![Keybind::new(Code::PageUp)],
|
jump_prev: vec![Keybind::new(Code::PageUp)],
|
||||||
};
|
};
|
||||||
|
@ -109,22 +119,36 @@ pub struct LogicalSize<T> {
|
||||||
pub height: T,
|
pub height: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> LogicalSize<T> {
|
impl Default for LogicalSize<f64> {
|
||||||
pub fn new(width: T, height: T) -> Self {
|
fn default() -> Self {
|
||||||
Self { width, height }
|
Self {
|
||||||
|
width: 800.0,
|
||||||
|
height: 400.0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _title() -> String {
|
||||||
|
"RMenu Application Launcher".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
/// GUI Desktop Window Configuration Settings
|
/// GUI Desktop Window Configuration Settings
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
pub struct WindowConfig {
|
pub struct WindowConfig {
|
||||||
|
#[serde(default = "_title")]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
#[serde(default = "LogicalSize::default")]
|
||||||
pub size: LogicalSize<f64>,
|
pub size: LogicalSize<f64>,
|
||||||
#[serde(default = "_true")]
|
#[serde(default = "_true")]
|
||||||
pub focus: bool,
|
pub focus: bool,
|
||||||
|
#[serde(default = "_false")]
|
||||||
pub decorate: bool,
|
pub decorate: bool,
|
||||||
|
#[serde(default = "_false")]
|
||||||
pub transparent: bool,
|
pub transparent: bool,
|
||||||
#[serde(default = "_true")]
|
#[serde(default = "_true")]
|
||||||
|
pub resizable: bool,
|
||||||
|
#[serde(default = "_true")]
|
||||||
pub always_top: bool,
|
pub always_top: bool,
|
||||||
pub fullscreen: Option<bool>,
|
pub fullscreen: Option<bool>,
|
||||||
pub dark_mode: Option<bool>,
|
pub dark_mode: Option<bool>,
|
||||||
|
@ -133,11 +157,12 @@ pub struct WindowConfig {
|
||||||
impl Default for WindowConfig {
|
impl Default for WindowConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: "RMenu - App Launcher".to_owned(),
|
title: _title(),
|
||||||
size: LogicalSize::new(700.0, 400.0),
|
size: LogicalSize::default(),
|
||||||
focus: true,
|
focus: true,
|
||||||
decorate: false,
|
decorate: false,
|
||||||
transparent: false,
|
transparent: false,
|
||||||
|
resizable: true,
|
||||||
always_top: true,
|
always_top: true,
|
||||||
fullscreen: None,
|
fullscreen: None,
|
||||||
dark_mode: None,
|
dark_mode: None,
|
||||||
|
@ -199,11 +224,6 @@ pub struct PluginConfig {
|
||||||
pub options: Option<Options>,
|
pub options: Option<Options>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn _true() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct SearchConfig {
|
pub struct SearchConfig {
|
||||||
|
@ -230,20 +250,42 @@ impl Default for SearchConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _page_size() -> usize {
|
||||||
|
50
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _page_load() -> f64 {
|
||||||
|
0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn _jump_dist() -> usize {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
/// Global RMenu Complete Configuration
|
/// Global RMenu Complete Configuration
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
#[serde(default = "_page_size")]
|
||||||
pub page_size: usize,
|
pub page_size: usize,
|
||||||
|
#[serde(default = "_page_load")]
|
||||||
pub page_load: f64,
|
pub page_load: f64,
|
||||||
|
#[serde(default = "_jump_dist")]
|
||||||
pub jump_dist: usize,
|
pub jump_dist: usize,
|
||||||
#[serde(default = "_true")]
|
#[serde(default = "_true")]
|
||||||
pub use_icons: bool,
|
pub use_icons: bool,
|
||||||
#[serde(default = "_true")]
|
#[serde(default = "_true")]
|
||||||
pub use_comments: bool,
|
pub use_comments: bool,
|
||||||
|
#[serde(default)]
|
||||||
pub search: SearchConfig,
|
pub search: SearchConfig,
|
||||||
|
#[serde(default)]
|
||||||
pub plugins: BTreeMap<String, PluginConfig>,
|
pub plugins: BTreeMap<String, PluginConfig>,
|
||||||
|
#[serde(default)]
|
||||||
pub keybinds: KeyConfig,
|
pub keybinds: KeyConfig,
|
||||||
|
#[serde(default)]
|
||||||
pub window: WindowConfig,
|
pub window: WindowConfig,
|
||||||
pub css: Option<String>,
|
pub css: Option<String>,
|
||||||
pub terminal: Option<String>,
|
pub terminal: Option<String>,
|
||||||
|
@ -252,9 +294,9 @@ pub struct Config {
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
page_size: 50,
|
page_size: _page_size(),
|
||||||
page_load: 0.8,
|
page_load: _page_load(),
|
||||||
jump_dist: 5,
|
jump_dist: _jump_dist(),
|
||||||
use_icons: true,
|
use_icons: true,
|
||||||
use_comments: true,
|
use_comments: true,
|
||||||
search: Default::default(),
|
search: Default::default(),
|
||||||
|
|
102
rmenu/src/gui.rs
102
rmenu/src/gui.rs
|
@ -173,31 +173,74 @@ impl<'a> AppState<'a> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Move Up a Number of Full Positions
|
||||||
fn move_up(&mut self, up: usize) -> Option<String> {
|
fn move_up(&mut self, up: usize) -> Option<String> {
|
||||||
self.pos = std::cmp::max(self.pos, up) - up;
|
self.pos = std::cmp::max(self.pos, up) - up;
|
||||||
|
self.subpos = 0;
|
||||||
Some(format!("setpos({})", self.pos))
|
Some(format!("setpos({})", self.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Move Down a Number of Full Positions
|
||||||
fn move_down(&mut self, down: usize) -> Option<String> {
|
fn move_down(&mut self, down: usize) -> Option<String> {
|
||||||
let max = (self.page + 1) * self.data.config.page_size;
|
let max = (self.page + 1) * self.data.config.page_size;
|
||||||
let n = std::cmp::max(self.results.len(), 1);
|
let n = std::cmp::max(self.results.len(), 1);
|
||||||
let end = std::cmp::min(max, n) - 1;
|
let end = std::cmp::min(max, n) - 1;
|
||||||
self.pos = std::cmp::min(self.pos + down, end);
|
self.pos = std::cmp::min(self.pos + down, end);
|
||||||
|
self.subpos = 0;
|
||||||
match self.append_results(false) {
|
match self.append_results(false) {
|
||||||
Some(operation) => Some(operation),
|
Some(operation) => Some(operation),
|
||||||
None => Some(format!("setpos({})", self.pos)),
|
None => Some(format!("setpos({})", self.pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move Next w/ Context of Sub-Menus
|
||||||
|
fn move_next(&mut self) -> Option<String> {
|
||||||
|
if let Some(entry) = self.results.get(self.pos) {
|
||||||
|
if self.subpos > 0 && self.subpos < entry.actions.len() - 1 {
|
||||||
|
self.subpos += 1;
|
||||||
|
return Some(format!("subpos({}, {})", self.pos, self.subpos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.move_down(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move Previous w/ Context of Sub-Menus
|
||||||
|
fn move_prev(&mut self) -> Option<String> {
|
||||||
|
if self.subpos > 1 {
|
||||||
|
self.subpos -= 1;
|
||||||
|
return Some(format!("subpos({}, {})", self.pos, self.subpos));
|
||||||
|
}
|
||||||
|
if self.subpos == 1 {
|
||||||
|
self.subpos = 0;
|
||||||
|
return Some(format!("setpos({})", self.pos));
|
||||||
|
}
|
||||||
|
self.move_up(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move Position to Submenu (if one Exists)
|
||||||
|
fn open_menu(&mut self) -> Option<String> {
|
||||||
|
if let Some(result) = self.results.get(self.pos) {
|
||||||
|
let newpos = self.subpos + 1;
|
||||||
|
if result.actions.len() > newpos {
|
||||||
|
self.subpos = newpos;
|
||||||
|
return Some(format!("subpos({}, {})", self.pos, self.subpos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close SubMenu (if one is Open)
|
||||||
|
fn close_menu(&mut self) -> Option<String> {
|
||||||
|
self.subpos = 0;
|
||||||
|
Some(format!("setpos({})", self.pos))
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle Search Event sent by UI
|
/// Handle Search Event sent by UI
|
||||||
fn search_event(&mut self, search: String) -> Option<String> {
|
fn search_event(&mut self, search: String) -> Option<String> {
|
||||||
let results = self.search(search);
|
let results = self.search(search);
|
||||||
Some(format!("update({results:?})"))
|
Some(format!("update({results:?})"))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add submenu access and selection
|
|
||||||
//TODO: put back main to reference actual config
|
//TODO: put back main to reference actual config
|
||||||
//TODO: update sway config to make borderless
|
//TODO: update sway config to make borderless
|
||||||
|
|
||||||
|
@ -212,15 +255,13 @@ impl<'a> AppState<'a> {
|
||||||
} else if matches(&keybinds.exit, &mods, &code) {
|
} else if matches(&keybinds.exit, &mods, &code) {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
} else if matches(&keybinds.move_next, &mods, &code) {
|
} else if matches(&keybinds.move_next, &mods, &code) {
|
||||||
self.move_down(1)
|
self.move_next()
|
||||||
} else if matches(&keybinds.move_prev, &mods, &code) {
|
} else if matches(&keybinds.move_prev, &mods, &code) {
|
||||||
self.move_up(1)
|
self.move_prev()
|
||||||
} else if matches(&keybinds.open_menu, &mods, &code) {
|
} else if matches(&keybinds.open_menu, &mods, &code) {
|
||||||
// k_updater.set_event(KeyEvent::OpenMenu);
|
self.open_menu()
|
||||||
None
|
|
||||||
} else if matches(&keybinds.close_menu, &mods, &code) {
|
} else if matches(&keybinds.close_menu, &mods, &code) {
|
||||||
// k_updater.set_event(KeyEvent::CloseMenu);
|
self.close_menu()
|
||||||
None
|
|
||||||
} else if matches(&keybinds.jump_next, &mods, &code) {
|
} else if matches(&keybinds.jump_next, &mods, &code) {
|
||||||
self.move_down(self.data.config.jump_dist)
|
self.move_down(self.data.config.jump_dist)
|
||||||
} else if matches(&keybinds.jump_prev, &mods, &code) {
|
} else if matches(&keybinds.jump_prev, &mods, &code) {
|
||||||
|
@ -303,20 +344,41 @@ impl<'a> AppState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update Gtk's Screen w/ Custom CSS to make Transparent
|
||||||
|
fn transparency_hack() {
|
||||||
|
use gdk_sys::gdk_screen_get_default;
|
||||||
|
use gtk_sys::*;
|
||||||
|
use std::ffi::CString;
|
||||||
|
// generate css-provider
|
||||||
|
let provider = unsafe { gtk_css_provider_new() };
|
||||||
|
// apply css to css-provider
|
||||||
|
let css = CString::new("* { background: transparent }").unwrap();
|
||||||
|
let clen = css.as_bytes().len();
|
||||||
|
let mut error = std::ptr::null_mut();
|
||||||
|
unsafe { gtk_css_provider_load_from_data(provider, css.as_ptr() as _, clen as _, &mut error) };
|
||||||
|
// retrieve screen and apply css- provider to screen
|
||||||
|
let prio = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION;
|
||||||
|
let screen = unsafe { gdk_screen_get_default() };
|
||||||
|
unsafe { gtk_style_context_add_provider_for_screen(screen, provider as _, prio as _) };
|
||||||
|
}
|
||||||
|
|
||||||
/// Run GUI Applcation via WebView
|
/// Run GUI Applcation via WebView
|
||||||
pub fn run(data: AppData) {
|
pub fn run(data: AppData) {
|
||||||
// build app-state
|
// build app-state
|
||||||
let mut state = AppState::new(&data);
|
let mut state = AppState::new(&data);
|
||||||
let html = state.render_index();
|
let html = state.render_index();
|
||||||
// spawn webview instance
|
// spawn webview instance
|
||||||
let size = &state.data.config.window.size;
|
let wcfg = &state.data.config.window;
|
||||||
web_view::builder()
|
let size = &wcfg.size;
|
||||||
|
let fullscreen = wcfg.fullscreen;
|
||||||
|
let transparent = wcfg.transparent;
|
||||||
|
let mut window = web_view::builder()
|
||||||
.title(&state.data.config.window.title)
|
.title(&state.data.config.window.title)
|
||||||
.content(Content::Html(html))
|
.content(Content::Html(html))
|
||||||
.frameless(!state.data.config.window.decorate)
|
.frameless(!wcfg.decorate)
|
||||||
.size(size.width as i32, size.height as i32)
|
.size(size.width as i32, size.height as i32)
|
||||||
.resizable(false)
|
.resizable(wcfg.resizable)
|
||||||
.debug(true)
|
.debug(cfg!(debug_assertions))
|
||||||
.user_data(())
|
.user_data(())
|
||||||
.invoke_handler(|webview, msg| {
|
.invoke_handler(|webview, msg| {
|
||||||
if let Some(js) = state.handle_event(msg) {
|
if let Some(js) = state.handle_event(msg) {
|
||||||
|
@ -324,6 +386,16 @@ pub fn run(data: AppData) {
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.run()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// manage transparency and fullscreen settings
|
||||||
|
if transparent {
|
||||||
|
window.set_color((0, 0, 0, 0));
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
transparency_hack();
|
||||||
|
}
|
||||||
|
if let Some(fullscreen) = fullscreen {
|
||||||
|
window.set_fullscreen(fullscreen);
|
||||||
|
}
|
||||||
|
window.run().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ mod icons;
|
||||||
mod search;
|
mod search;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use config::{CacheSetting, PluginConfig};
|
|
||||||
use rmenu_plugin::{self_exe, Entry};
|
use rmenu_plugin::{self_exe, Entry};
|
||||||
|
|
||||||
static CONFIG_DIR: &'static str = "~/.config/rmenu/";
|
static CONFIG_DIR: &'static str = "~/.config/rmenu/";
|
||||||
|
@ -42,26 +41,7 @@ fn main() -> cli::Result<()> {
|
||||||
|
|
||||||
// parse cli and retrieve values for app
|
// parse cli and retrieve values for app
|
||||||
let mut cli = cli::Args::parse();
|
let mut cli = cli::Args::parse();
|
||||||
// let mut config = cli.get_config()?;
|
let mut config = cli.get_config()?;
|
||||||
let mut config = crate::config::Config::default();
|
|
||||||
config.plugins.insert(
|
|
||||||
"run".to_owned(),
|
|
||||||
PluginConfig {
|
|
||||||
exec: vec!["/home/andrew/.config/rmenu/rmenu-run".to_owned()],
|
|
||||||
cache: CacheSetting::OnLogin,
|
|
||||||
placeholder: None,
|
|
||||||
options: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
config.plugins.insert(
|
|
||||||
"drun".to_owned(),
|
|
||||||
PluginConfig {
|
|
||||||
exec: vec!["/home/andrew/.config/rmenu/rmenu-desktop".to_owned()],
|
|
||||||
cache: CacheSetting::OnLogin,
|
|
||||||
placeholder: None,
|
|
||||||
options: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let entries = cli.get_entries(&mut config)?;
|
let entries = cli.get_entries(&mut config)?;
|
||||||
let css = cli.get_css(&config);
|
let css = cli.get_css(&config);
|
||||||
let theme = cli.get_theme();
|
let theme = cli.get_theme();
|
||||||
|
|
|
@ -31,18 +31,19 @@
|
||||||
{%else%}
|
{%else%}
|
||||||
<div class="entry">{{ entry.name|safe }}</div>
|
<div class="entry">{{ entry.name|safe }}</div>
|
||||||
{%endif%}
|
{%endif%}
|
||||||
<div id="result-{{ i }}-actions" class="actions">
|
|
||||||
{%for action in entry.actions%}
|
|
||||||
<div class="action">
|
|
||||||
<div class="action-name">{{ action.name|safe }}</div>
|
|
||||||
<div class="action-comment">
|
|
||||||
{%- if let Some(comment) = action.comment %}
|
|
||||||
{{ comment|safe }}
|
|
||||||
{%endif%}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{%endfor%}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="result-{{ i }}-actions" class="actions">
|
||||||
|
{%for n in 1..entry.actions.len() %}
|
||||||
|
{% let action = entry.actions[n] %}
|
||||||
|
<div id="result-{{ i }}-action-{{ n }}" class="action">
|
||||||
|
<div class="action-name">{{ action.name|safe }}</div>
|
||||||
|
<div class="action-comment">
|
||||||
|
{%- if let Some(comment) = action.comment %}
|
||||||
|
{{ comment|safe }}
|
||||||
|
{%endif%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%endfor%}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
|
|
4
rmenu/test.css
Normal file
4
rmenu/test.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
* {
|
||||||
|
background: transparent;
|
||||||
|
}
|
|
@ -56,7 +56,8 @@ input {
|
||||||
justify-content: left;
|
justify-content: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result > div, .action > div {
|
.result div,
|
||||||
|
.action div {
|
||||||
margin: 2px 5px;
|
margin: 2px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
|
|
||||||
|
const input = document.getElementById("search");
|
||||||
const results = document.getElementById("results");
|
const results = document.getElementById("results");
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
|
@ -24,8 +25,9 @@ function search(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send keydown event back to rust
|
/// send keydown event back to rust
|
||||||
function keydown({ key, ctrlKey, shiftKey }) {
|
function keydown(e) {
|
||||||
_send("keydown", { key, "ctrl": ctrlKey, "shift": shiftKey });
|
(e.key == "ArrowUp" || e.key == "ArrowDown") && e.preventDefault();
|
||||||
|
_send("keydown", { "key": e.key, "ctrl": e.ctrlKey, "shift": e.shiftKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send click event back to rust
|
/// send click event back to rust
|
||||||
|
@ -44,17 +46,22 @@ function scroll() {
|
||||||
_send("scroll", { "y": results.scrollTop, "maxy": height });
|
_send("scroll", { "y": results.scrollTop, "maxy": height });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove active class from all current objects
|
||||||
|
function reset() {
|
||||||
|
const classes = ["active", "selected"];
|
||||||
|
for (const cname of classes) {
|
||||||
|
const selected = document.getElementsByClassName(cname);
|
||||||
|
const elems = Array.from(selected);
|
||||||
|
elems.forEach((e) => e.classList.remove(cname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// set selected-result position
|
/// set selected-result position
|
||||||
function setpos(pos, smooth = false) {
|
function setpos(pos, smooth = false) {
|
||||||
// remove selected class from all current objects
|
reset();
|
||||||
const selected = document.getElementsByClassName("selected");
|
|
||||||
const elems = Array.from(selected);
|
|
||||||
elems.forEach((e) => e.classList.remove("selected"));
|
|
||||||
// add selected to current position
|
// add selected to current position
|
||||||
let current = document.getElementById(`result-${pos}`);
|
const current = document.getElementById(`result-${pos}`);
|
||||||
if (!current) {
|
if (!current) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
current.classList.add("selected");
|
current.classList.add("selected");
|
||||||
// ensure selected always within view
|
// ensure selected always within view
|
||||||
current.scrollIntoView({
|
current.scrollIntoView({
|
||||||
|
@ -64,6 +71,19 @@ function setpos(pos, smooth = false) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set selected-result subposition
|
||||||
|
function subpos(pos, subpos) {
|
||||||
|
reset();
|
||||||
|
// activate submenu
|
||||||
|
const actions = document.getElementById(`result-${pos}-actions`);
|
||||||
|
if (!actions) return;
|
||||||
|
actions.classList.add("active");
|
||||||
|
// select current subposition
|
||||||
|
const action = document.getElementById(`result-${pos}-action-${subpos}`);
|
||||||
|
if (!action) return;
|
||||||
|
action.classList.add("selected");
|
||||||
|
}
|
||||||
|
|
||||||
/// Update Results HTML
|
/// Update Results HTML
|
||||||
function update(html) {
|
function update(html) {
|
||||||
results.innerHTML = html;
|
results.innerHTML = html;
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
background-color: #24242480;
|
/* background-color: #383c4a2b !important; */
|
||||||
|
background-color: #24242480 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
@ -26,7 +27,7 @@ input {
|
||||||
border: 1px;
|
border: 1px;
|
||||||
border-color: #f5f5f540;
|
border-color: #f5f5f540;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: #363636;
|
background-color: #363636 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder {
|
input::placeholder {
|
||||||
|
|
Loading…
Reference in a new issue