mirror of
https://github.com/neovide/neovide.git
synced 2026-09-10 07:16:25 -04:00
Merge branch 'main' into fix/3532-config-error-recovery
This commit is contained in:
commit
986fcb8f64
15
lua/init.lua
15
lua/init.lua
|
|
@ -430,18 +430,19 @@ vim.api.nvim_create_autocmd("OptionSet", {
|
|||
|
||||
notify_intro_state()
|
||||
|
||||
pcall(vim.api.nvim_create_autocmd, 'Progress', {
|
||||
group = vim.api.nvim_create_augroup('NeovideProgressBar', { clear = true }),
|
||||
desc = 'Forward progress events to Neovide',
|
||||
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
|
||||
if ev.data then
|
||||
progress_bar({
|
||||
percent = ev.data.percent or 0,
|
||||
id = ev.data.id,
|
||||
source = ev.data.source,
|
||||
status = ev.data.status,
|
||||
percent = ev.data.percent,
|
||||
title = ev.data.title or "",
|
||||
message = ev.data.message or "",
|
||||
})
|
||||
else
|
||||
progress_bar({ percent = 100 })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use strum::AsRefStr;
|
|||
use super::RestartDetails;
|
||||
use crate::{
|
||||
editor::{Colors, CursorMode, CursorShape, Style, UnderlineStyle},
|
||||
window::UserEvent,
|
||||
window::{ProgressBarUpdate, UserEvent},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -1144,15 +1144,40 @@ pub fn parse_redraw_event(event_value: Value) -> Result<Vec<RedrawEvent>> {
|
|||
Ok(parsed_events)
|
||||
}
|
||||
|
||||
fn parse_map_field<'a>(map: &'a [(Value, Value)], key: &str) -> Option<&'a Value> {
|
||||
map.iter().find_map(|(name, value)| (name.as_str() == Some(key)).then_some(value))
|
||||
}
|
||||
|
||||
fn parse_progress_id(value: &Value) -> Option<String> {
|
||||
value
|
||||
.as_str()
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| value.as_i64().map(|value| value.to_string()))
|
||||
.or_else(|| value.as_u64().map(|value| value.to_string()))
|
||||
}
|
||||
|
||||
fn parse_progress_string(value: &Value) -> Option<String> {
|
||||
value.as_str().map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
fn parse_progress_percent(value: &Value) -> Option<f32> {
|
||||
value
|
||||
.as_f64()
|
||||
.map(|value| value as f32)
|
||||
.or_else(|| value.as_i64().map(|value| value as f32))
|
||||
.or_else(|| value.as_u64().map(|value| value as f32))
|
||||
}
|
||||
|
||||
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;
|
||||
let update = ProgressBarUpdate {
|
||||
id: parse_map_field(map, "id").and_then(parse_progress_id),
|
||||
source: parse_map_field(map, "source").and_then(parse_progress_string),
|
||||
status: parse_map_field(map, "status").and_then(parse_progress_string),
|
||||
percent: parse_map_field(map, "percent").and_then(parse_progress_percent),
|
||||
};
|
||||
|
||||
Some(UserEvent::ShowProgressBar { percent })
|
||||
Some(UserEvent::ShowProgressBar { update })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ impl Renderer {
|
|||
|
||||
let progress_bar_settings = self.settings.get::<ProgressBarSettings>();
|
||||
self.progress_bar.animate(&progress_bar_settings, dt);
|
||||
animating |= self.progress_bar.is_animating();
|
||||
animating |= self.progress_bar.is_active();
|
||||
|
||||
animating
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ use crate::{renderer::GridRenderer, settings::ParseFromValue};
|
|||
use neovide_derive::SettingGroup;
|
||||
use skia_safe::{Canvas, Color4f, Paint, Rect};
|
||||
|
||||
/// Minimum active time required before the progress earns to be shown.
|
||||
const REVEAL_DELAY: f32 = 0.1;
|
||||
|
||||
#[derive(Clone, SettingGroup)]
|
||||
#[setting_prefix = "progress_bar"]
|
||||
pub struct ProgressBarSettings {
|
||||
|
|
@ -21,36 +24,105 @@ impl Default for ProgressBarSettings {
|
|||
|
||||
enum ProgressBarState {
|
||||
Idle,
|
||||
/// Progress has started, but the bar has not been shown yet.
|
||||
///
|
||||
/// it could happen that a producer has a very short progress or just sent
|
||||
/// only a final completion update showing updates that could take a 0-100
|
||||
/// animation bar after its work has been finished or by an unknown behavior,
|
||||
/// adding actually only noise without giving useful user progress feedback.
|
||||
///
|
||||
/// we keep the bar hidden until progress remains active based on a
|
||||
/// REVEAL_DELAY making it much more meaninful to show progress that
|
||||
/// has been earned to be shown instead.
|
||||
Pending {
|
||||
elapsed: f32,
|
||||
},
|
||||
Animating,
|
||||
Completing { completion_time: Instant },
|
||||
Completing {
|
||||
completion_time: Instant,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct ProgressBar {
|
||||
current_percent: f32,
|
||||
target_percent: f32,
|
||||
id: Option<String>,
|
||||
state: ProgressBarState,
|
||||
}
|
||||
|
||||
impl ProgressBar {
|
||||
pub fn new() -> Self {
|
||||
Self { current_percent: 0.0, target_percent: 0.0, state: ProgressBarState::Idle }
|
||||
Self { current_percent: 0.0, target_percent: 0.0, id: None, state: ProgressBarState::Idle }
|
||||
}
|
||||
|
||||
pub fn is_animating(&self) -> bool {
|
||||
pub fn is_active(&self) -> bool {
|
||||
!matches!(self.state, ProgressBarState::Idle)
|
||||
}
|
||||
|
||||
pub fn start(&mut self, percent: f32) {
|
||||
fn is_visible(&self) -> bool {
|
||||
matches!(self.state, ProgressBarState::Animating | ProgressBarState::Completing { .. })
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.current_percent = 0.0;
|
||||
self.target_percent = 0.0;
|
||||
self.id = None;
|
||||
self.state = ProgressBarState::Idle;
|
||||
}
|
||||
|
||||
fn set_target_percent(&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;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, id: Option<&str>, percent: f32) {
|
||||
if percent >= 100.0 {
|
||||
self.finish(id);
|
||||
return;
|
||||
}
|
||||
|
||||
let is_idle = matches!(self.state, ProgressBarState::Idle);
|
||||
let is_completing = matches!(self.state, ProgressBarState::Completing { .. });
|
||||
|
||||
self.id = id.map(ToOwned::to_owned);
|
||||
self.set_target_percent(percent);
|
||||
|
||||
if is_idle {
|
||||
self.state = ProgressBarState::Pending { elapsed: 0.0 };
|
||||
} else if is_completing {
|
||||
self.state = ProgressBarState::Animating;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(&mut self, id: Option<&str>) {
|
||||
let current_id = self.id.as_deref();
|
||||
let id_mismatch = id.zip(current_id).is_some_and(|(id, current_id)| id != current_id);
|
||||
|
||||
if !self.is_active() || id_mismatch {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(&self.state, ProgressBarState::Pending { .. }) {
|
||||
self.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
self.id = id.or(current_id).map(ToOwned::to_owned);
|
||||
self.set_target_percent(100.0);
|
||||
self.state = ProgressBarState::Animating;
|
||||
}
|
||||
|
||||
pub fn animate(&mut self, settings: &ProgressBarSettings, dt: f32) {
|
||||
match &self.state {
|
||||
match &mut self.state {
|
||||
ProgressBarState::Idle => {}
|
||||
ProgressBarState::Pending { elapsed } => {
|
||||
*elapsed += dt;
|
||||
if *elapsed >= REVEAL_DELAY {
|
||||
self.state = ProgressBarState::Animating;
|
||||
}
|
||||
}
|
||||
ProgressBarState::Animating => {
|
||||
if self.current_percent < self.target_percent {
|
||||
self.current_percent += settings.animation_speed * dt;
|
||||
|
|
@ -63,10 +135,7 @@ impl ProgressBar {
|
|||
}
|
||||
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;
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +148,7 @@ impl ProgressBar {
|
|||
grid_renderer: &GridRenderer,
|
||||
grid_size: crate::units::GridSize<u32>,
|
||||
) {
|
||||
if !self.is_animating() || !settings.enabled {
|
||||
if !self.is_visible() || !settings.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -499,7 +499,7 @@ impl Application {
|
|||
|
||||
fn process_buffered_draw_commands(&mut self, window_id: WindowId) {
|
||||
let pending_batches = match self.render_states.get_mut(&window_id) {
|
||||
Some(state) => state.pending_draw_commands.drain(..).collect::<Vec<_>>(),
|
||||
Some(state) => std::mem::take(&mut state.pending_draw_commands),
|
||||
None => return,
|
||||
};
|
||||
if !pending_batches.is_empty() {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,29 @@ pub struct Pressure {
|
|||
stage: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ProgressBarUpdate {
|
||||
pub id: Option<String>,
|
||||
pub source: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub percent: Option<f32>,
|
||||
}
|
||||
|
||||
impl ProgressBarUpdate {
|
||||
/// neovim stable and nightly encode the same save event differently.
|
||||
/// for example stable uses id = "bufwrite" with percent = 0 while nightly uses
|
||||
/// id = "nvim.bufwrite [file]" without a percent field.
|
||||
/// A determinate percentage on either shape is not treated as built-in save progress.
|
||||
pub fn is_neovim_bufwrite(&self) -> bool {
|
||||
self.source.as_deref() == Some("nvim")
|
||||
&& match self.id.as_deref() {
|
||||
Some("bufwrite") => self.percent == Some(0.0), // stable
|
||||
Some(id) if id.starts_with("nvim.bufwrite ") => self.percent.is_none(), // nightly
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ForceClickKind {
|
||||
|
|
@ -158,7 +181,7 @@ pub enum UserEvent {
|
|||
},
|
||||
NeovimRestart(RestartDetails),
|
||||
ShowProgressBar {
|
||||
percent: f32,
|
||||
update: ProgressBarUpdate,
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
CreateWindow,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use approx::AbsDiffEq;
|
|||
use super::settings::CornerPreference;
|
||||
use super::{
|
||||
EventPayload, EventTarget, KeyboardManager, MessageSelectionEvent, MouseManager, OverlayEvent,
|
||||
RouteId, UserEvent, WindowCommand, WindowSettings, WindowSettingsChanged, WindowSize,
|
||||
ProgressBarUpdate, RouteId, UserEvent, WindowCommand, WindowSettings, WindowSettingsChanged,
|
||||
WindowSize,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
@ -1243,8 +1244,8 @@ impl WinitWindowWrapper {
|
|||
UserEvent::MacShortcut(command) => {
|
||||
self.handle_mac_shortcut(command);
|
||||
}
|
||||
UserEvent::ShowProgressBar { percent, .. } => {
|
||||
self.handle_progress_bar(target, percent);
|
||||
UserEvent::ShowProgressBar { update } => {
|
||||
self.handle_progress_bar(target, update);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -2246,13 +2247,23 @@ impl WinitWindowWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_progress_bar(&mut self, target: EventTarget, percent: f32) {
|
||||
fn handle_progress_bar(&mut self, target: EventTarget, update: ProgressBarUpdate) {
|
||||
tracy_zone!("handle_progress_bar");
|
||||
if update.is_neovim_bufwrite() {
|
||||
return;
|
||||
}
|
||||
|
||||
let window_ids = self.window_ids_for_target(target);
|
||||
for window_id in window_ids {
|
||||
if let Some(route) = self.routes.get(&window_id) {
|
||||
let mut renderer = route.window.renderer.borrow_mut();
|
||||
renderer.progress_bar.start(percent);
|
||||
match update.percent {
|
||||
Some(percent) if percent >= 100.0 => {
|
||||
renderer.progress_bar.finish(update.id.as_deref());
|
||||
}
|
||||
Some(percent) => renderer.progress_bar.start(update.id.as_deref(), percent),
|
||||
None => renderer.progress_bar.finish(update.id.as_deref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue