mirror of
https://github.com/neovide/neovide.git
synced 2026-09-10 07:16:25 -04:00
feat: add animated progress bar with nvim_echo event support (#3279)
This commit is contained in:
parent
71abe21672
commit
faf1d8ad37
21
lua/init.lua
21
lua/init.lua
|
|
@ -166,4 +166,25 @@ vim.api.nvim_create_user_command("NeovideConfig", function()
|
|||
end
|
||||
end, {})
|
||||
|
||||
local function progress_bar(data)
|
||||
-- Wrap inside pcall to avoid errors if Neovide disconnects.
|
||||
pcall(rpcnotify, "neovide.progress_bar", data)
|
||||
end
|
||||
|
||||
pcall(vim.api.nvim_create_autocmd, 'Progress', {
|
||||
group = vim.api.nvim_create_augroup('NeovideProgressBar', { clear = true }),
|
||||
desc = 'Forward progress events to Neovide',
|
||||
callback = function(ev)
|
||||
if ev.data and ev.data.status == 'running' then
|
||||
progress_bar({
|
||||
percent = ev.data.percent or 0,
|
||||
title = ev.data.title or "",
|
||||
message = ev.data.message or "",
|
||||
})
|
||||
else
|
||||
progress_bar({ percent = 100 })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
_G["neovide"] = M
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ use rmpv::Value;
|
|||
use skia_safe::Color4f;
|
||||
use strum::AsRefStr;
|
||||
|
||||
use crate::editor::{Colors, CursorMode, CursorShape, Style, UnderlineStyle};
|
||||
use crate::{
|
||||
editor::{Colors, CursorMode, CursorShape, Style, UnderlineStyle},
|
||||
window::UserEvent,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ParseError {
|
||||
|
|
@ -1063,3 +1066,14 @@ pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
|
|||
|
||||
Ok(parsed_events)
|
||||
}
|
||||
|
||||
pub fn parse_progress_bar_event(value: Option<&Value>) -> Option<UserEvent> {
|
||||
let map = value.filter(|v| matches!(v, Value::Map(_)))?.as_map()?;
|
||||
let percent = map
|
||||
.iter()
|
||||
.find(|(key, _)| key.as_str() == Some("percent"))
|
||||
.and_then(|(_, value)| value.as_f64())
|
||||
.unwrap_or(0.0) as f32;
|
||||
|
||||
Some(UserEvent::ShowProgressBar { percent })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
bridge::{
|
||||
clipboard::{get_clipboard_contents, set_clipboard_contents},
|
||||
events::parse_redraw_event,
|
||||
send_ui, NeovimWriter, ParallelCommand, RedrawEvent,
|
||||
parse_progress_bar_event, send_ui, NeovimWriter, ParallelCommand, RedrawEvent,
|
||||
},
|
||||
error_handling::ResultPanicExplanation,
|
||||
running_tracker::RunningTracker,
|
||||
|
|
@ -134,6 +134,18 @@ impl Handler for NeovimHandler {
|
|||
let _ = self.sender.send(RedrawEvent::NeovideSetRedraw(value));
|
||||
}
|
||||
}
|
||||
"neovide.progress_bar" => {
|
||||
parse_progress_bar_event(arguments.first())
|
||||
.map(|event| {
|
||||
let _ = self.proxy.lock().unwrap().send_event(event);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::info!(
|
||||
"Failed to parse neovide.progress_bar event data: {:?}",
|
||||
arguments
|
||||
);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,7 +225,6 @@ impl ParallelCommand {
|
|||
ParallelCommand::DisplayAvailableFonts(fonts) => display_available_fonts(nvim, fonts)
|
||||
.await
|
||||
.context("DisplayAvailableFonts failed"),
|
||||
|
||||
ParallelCommand::ShowError { lines } => {
|
||||
// nvim.err_write(&message).await.ok();
|
||||
// NOTE: https://github.com/neovim/neovim/issues/5067
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ use backtrace::Backtrace;
|
|||
use bridge::NeovimRuntime;
|
||||
use cmd_line::CmdLineSettings;
|
||||
use error_handling::handle_startup_errors;
|
||||
use renderer::{cursor_renderer::CursorSettings, RendererSettings};
|
||||
use renderer::{
|
||||
cursor_renderer::CursorSettings, progress_bar::ProgressBarSettings, RendererSettings,
|
||||
};
|
||||
use running_tracker::RunningTracker;
|
||||
use window::{
|
||||
create_event_loop, determine_window_size, UpdateLoop, UserEvent, WindowSettings, WindowSize,
|
||||
|
|
@ -215,6 +217,7 @@ fn setup(
|
|||
settings.register::<WindowSettings>();
|
||||
settings.register::<RendererSettings>();
|
||||
settings.register::<CursorSettings>();
|
||||
settings.register::<ProgressBarSettings>();
|
||||
|
||||
let config = Config::init();
|
||||
Config::watch_config_file(config.clone(), proxy.clone());
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub mod fonts;
|
|||
pub mod grid_renderer;
|
||||
pub mod opengl;
|
||||
pub mod profiler;
|
||||
pub mod progress_bar;
|
||||
mod rendered_layer;
|
||||
mod rendered_window;
|
||||
mod vsync;
|
||||
|
|
@ -23,6 +24,7 @@ use std::{
|
|||
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use progress_bar::{ProgressBar, ProgressBarSettings};
|
||||
use skia_safe::Canvas;
|
||||
|
||||
use winit::{
|
||||
|
|
@ -153,6 +155,8 @@ pub struct Renderer {
|
|||
pub grid_renderer: GridRenderer,
|
||||
current_mode: EditorMode,
|
||||
|
||||
pub progress_bar: ProgressBar,
|
||||
|
||||
rendered_windows: HashMap<u64, RenderedWindow>,
|
||||
pub window_regions: Vec<WindowDrawDetails>,
|
||||
|
||||
|
|
@ -186,6 +190,8 @@ impl Renderer {
|
|||
|
||||
let profiler = profiler::Profiler::new(12.0, settings.clone());
|
||||
|
||||
let progress_bar = ProgressBar::new();
|
||||
|
||||
Renderer {
|
||||
rendered_windows,
|
||||
cursor_renderer,
|
||||
|
|
@ -193,6 +199,7 @@ impl Renderer {
|
|||
current_mode,
|
||||
window_regions,
|
||||
profiler,
|
||||
progress_bar,
|
||||
os_scale_factor,
|
||||
user_scale_factor,
|
||||
settings,
|
||||
|
|
@ -327,8 +334,18 @@ impl Renderer {
|
|||
|
||||
self.profiler.draw(root_canvas, dt);
|
||||
|
||||
let grid_size = self.get_grid_size();
|
||||
|
||||
root_canvas.restore();
|
||||
|
||||
let progress_bar_settings = self.settings.get::<ProgressBarSettings>();
|
||||
self.progress_bar.draw(
|
||||
&progress_bar_settings,
|
||||
root_canvas,
|
||||
&self.grid_renderer,
|
||||
grid_size,
|
||||
);
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
plot_skia_cache();
|
||||
}
|
||||
|
|
@ -368,6 +385,10 @@ impl Renderer {
|
|||
.cursor_renderer
|
||||
.animate(&self.current_mode, &self.grid_renderer, dt);
|
||||
|
||||
let progress_bar_settings = self.settings.get::<ProgressBarSettings>();
|
||||
self.progress_bar.animate(&progress_bar_settings, dt);
|
||||
animating |= self.progress_bar.is_animating();
|
||||
|
||||
animating
|
||||
}
|
||||
|
||||
|
|
|
|||
114
src/renderer/progress_bar.rs
Normal file
114
src/renderer/progress_bar.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use crate::{renderer::GridRenderer, settings::ParseFromValue};
|
||||
use neovide_derive::SettingGroup;
|
||||
use skia_safe::{Canvas, Color4f, Paint, Rect};
|
||||
|
||||
#[derive(Clone, SettingGroup)]
|
||||
#[setting_prefix = "progress_bar"]
|
||||
pub struct ProgressBarSettings {
|
||||
pub enabled: bool,
|
||||
pub height: f32,
|
||||
pub animation_speed: f32,
|
||||
pub hide_delay: f32,
|
||||
}
|
||||
|
||||
impl Default for ProgressBarSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
height: 3.0,
|
||||
animation_speed: 100.0,
|
||||
hide_delay: 0.5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ProgressBarState {
|
||||
Idle,
|
||||
Animating,
|
||||
Completing { completion_time: Instant },
|
||||
}
|
||||
|
||||
pub struct ProgressBar {
|
||||
current_percent: f32,
|
||||
target_percent: f32,
|
||||
state: ProgressBarState,
|
||||
}
|
||||
|
||||
impl ProgressBar {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_percent: 0.0,
|
||||
target_percent: 0.0,
|
||||
state: ProgressBarState::Idle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_animating(&self) -> bool {
|
||||
!matches!(self.state, ProgressBarState::Idle)
|
||||
}
|
||||
|
||||
pub fn start(&mut self, percent: f32) {
|
||||
self.target_percent = percent.clamp(0.0, 100.0);
|
||||
if self.target_percent < self.current_percent {
|
||||
self.current_percent = self.target_percent;
|
||||
}
|
||||
self.state = ProgressBarState::Animating;
|
||||
}
|
||||
|
||||
pub fn animate(&mut self, settings: &ProgressBarSettings, dt: f32) {
|
||||
match &self.state {
|
||||
ProgressBarState::Idle => {}
|
||||
ProgressBarState::Animating => {
|
||||
if self.current_percent < self.target_percent {
|
||||
self.current_percent += settings.animation_speed * dt;
|
||||
// here we clamp to the target to prevent overshooting.
|
||||
self.current_percent = self.current_percent.min(self.target_percent);
|
||||
}
|
||||
if self.current_percent >= 100.0 {
|
||||
self.state = ProgressBarState::Completing {
|
||||
completion_time: Instant::now(),
|
||||
};
|
||||
}
|
||||
}
|
||||
ProgressBarState::Completing { completion_time } => {
|
||||
if completion_time.elapsed().as_secs_f32() > settings.hide_delay {
|
||||
self.state = ProgressBarState::Idle;
|
||||
// Reset percents for next time
|
||||
self.current_percent = 0.0;
|
||||
self.target_percent = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&self,
|
||||
settings: &ProgressBarSettings,
|
||||
canvas: &Canvas,
|
||||
grid_renderer: &GridRenderer,
|
||||
grid_size: crate::units::GridSize<u32>,
|
||||
) {
|
||||
if !self.is_animating() || !settings.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let width = grid_size.width as f32 * grid_renderer.grid_scale.width();
|
||||
let height = settings.height;
|
||||
let x = 0.0;
|
||||
let y = 0.0;
|
||||
let foreground_color = grid_renderer
|
||||
.default_style
|
||||
.colors
|
||||
.foreground
|
||||
.unwrap()
|
||||
.to_color();
|
||||
|
||||
let mut paint = Paint::new(Color4f::from(foreground_color), None);
|
||||
paint.set_anti_alias(true);
|
||||
|
||||
let rect = Rect::from_xywh(x, y, width * (self.current_percent / 100.0), height);
|
||||
canvas.draw_rect(rect, &paint);
|
||||
}
|
||||
}
|
||||
|
|
@ -189,6 +189,7 @@ pub enum SettingsChanged {
|
|||
Window(crate::window::WindowSettingsChanged),
|
||||
Cursor(crate::renderer::cursor_renderer::CursorSettingsChanged),
|
||||
Renderer(crate::renderer::RendererSettingsChanged),
|
||||
ProgressBar(crate::renderer::progress_bar::ProgressBarSettingsChanged),
|
||||
#[cfg(test)]
|
||||
Test(tests::TestSettingsChanged),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ pub enum UserEvent {
|
|||
#[allow(dead_code)]
|
||||
RedrawRequested,
|
||||
NeovimExited,
|
||||
ShowProgressBar {
|
||||
percent: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Vec<DrawCommand>> for UserEvent {
|
||||
|
|
|
|||
|
|
@ -432,6 +432,9 @@ impl WinitWindowWrapper {
|
|||
UserEvent::ConfigsChanged(config) => {
|
||||
self.handle_config_changed(*config);
|
||||
}
|
||||
UserEvent::ShowProgressBar { percent, .. } => {
|
||||
self.renderer.progress_bar.start(percent);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,6 +447,34 @@ When scrolling more than one screen at a time, only this many lines at the end o
|
|||
will be animated. Set it to 0 to snap to the final position without any animation, or to something
|
||||
big like 9999 to always scroll the whole screen, much like Neovide <= 0.10.4 did.
|
||||
|
||||
#### Progress Bar
|
||||
|
||||
VimScript:
|
||||
|
||||
```vim
|
||||
let g:neovide_progress_bar_enabled = v:true
|
||||
let g:neovide_progress_bar_height = 5.0
|
||||
let g:neovide_progress_bar_animation_speed = 200.0
|
||||
let g:neovide_progress_bar_hide_delay = 0.2
|
||||
```
|
||||
|
||||
Lua:
|
||||
|
||||
```lua
|
||||
vim.g.neovide_progress_bar_enabled = true
|
||||
vim.g.neovide_progress_bar_height = 5.0
|
||||
vim.g.neovide_progress_bar_animation_speed = 200.0
|
||||
vim.g.neovide_progress_bar_hide_delay = 0.2
|
||||
```
|
||||
|
||||
**Unreleased yet.**
|
||||
|
||||
- `g:neovide_progress_bar_enabled` sets whether the progress bar is enabled.
|
||||
- `g:neovide_progress_bar_height` sets the height of the progress bar in pixels.
|
||||
- `g:neovide_progress_bar_animation_speed` sets the speed of the progress bar animation.
|
||||
- `g:neovide_progress_bar_hide_delay` sets the delay in seconds before the progress bar is
|
||||
hidden after reaching 100%.
|
||||
|
||||
#### Hiding the mouse when typing
|
||||
|
||||
VimScript:
|
||||
|
|
|
|||
Loading…
Reference in a new issue