mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 03:25:23 +00:00
refactor(startup): improve overall startup time and remove first frames artifacts (#408)
Fixes #406
This commit is contained in:
parent
5ee891230c
commit
3a5b5ec0cc
@ -272,6 +272,16 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
match self {
|
||||
#(
|
||||
#enum_name::#variant_names(ref channel) => {
|
||||
channel.supports_preview()
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -146,6 +146,10 @@ impl OnAir for Channel {
|
||||
}
|
||||
|
||||
fn shutdown(&self) {}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
|
@ -213,6 +213,10 @@ impl OnAir for Channel {
|
||||
}
|
||||
|
||||
fn shutdown(&self) {}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
self.preview_kind != PreviewKind::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
|
||||
|
@ -142,6 +142,10 @@ impl OnAir for Channel {
|
||||
fn shutdown(&self) {
|
||||
self.crawl_handle.abort();
|
||||
}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
|
@ -131,4 +131,8 @@ impl OnAir for Channel {
|
||||
}
|
||||
|
||||
fn shutdown(&self) {}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +148,10 @@ impl OnAir for Channel {
|
||||
fn shutdown(&self) {
|
||||
self.crawl_handle.abort();
|
||||
}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
|
@ -113,6 +113,10 @@ impl OnAir for Channel {
|
||||
debug!("Shutting down git repos channel");
|
||||
self.crawl_handle.abort();
|
||||
}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ignored_paths() -> Vec<PathBuf> {
|
||||
|
@ -84,6 +84,9 @@ pub trait OnAir: Send {
|
||||
|
||||
/// Turn off
|
||||
fn shutdown(&self);
|
||||
|
||||
/// Whether this channel supports previewing entries.
|
||||
fn supports_preview(&self) -> bool;
|
||||
}
|
||||
|
||||
/// The available television channels.
|
||||
|
@ -181,4 +181,8 @@ impl OnAir for RemoteControl {
|
||||
}
|
||||
|
||||
fn shutdown(&self) {}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -120,4 +120,8 @@ impl OnAir for Channel {
|
||||
}
|
||||
|
||||
fn shutdown(&self) {}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
self.preview_type != PreviewType::None
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +245,10 @@ impl OnAir for Channel {
|
||||
fn shutdown(&self) {
|
||||
self.crawl_handle.abort();
|
||||
}
|
||||
|
||||
fn supports_preview(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The maximum file size we're willing to search in.
|
||||
|
@ -6,7 +6,7 @@ use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
action::Action,
|
||||
channels::entry::{Entry, PreviewType, ENTRY_PLACEHOLDER},
|
||||
channels::entry::Entry,
|
||||
config::Config,
|
||||
picker::Picker,
|
||||
preview::PreviewState,
|
||||
@ -22,6 +22,9 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// The state of the current television channel.
|
||||
///
|
||||
/// This struct is passed along to the UI thread as part of the `TvState` struct.
|
||||
pub struct ChannelState {
|
||||
pub current_channel_name: String,
|
||||
pub selected_entries: FxHashSet<Entry>,
|
||||
@ -57,6 +60,9 @@ impl Hash for ChannelState {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
/// The state of the main thread `Television` struct.
|
||||
///
|
||||
/// This struct is passed along to the UI thread as part of the `Ctx` struct.
|
||||
pub struct TvState {
|
||||
pub mode: Mode,
|
||||
pub selected_entry: Option<Entry>,
|
||||
@ -91,6 +97,11 @@ impl TvState {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A drawing context that holds the current state of the application.
|
||||
///
|
||||
/// This is used as a message passing object between the main thread
|
||||
/// and the UI thread and should contain all the information needed to
|
||||
/// draw a frame.
|
||||
pub struct Ctx {
|
||||
pub tv_state: TvState,
|
||||
pub config: Config,
|
||||
@ -152,15 +163,24 @@ impl Ord for Ctx {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the current UI frame based on the given context.
|
||||
///
|
||||
/// This function is responsible for drawing the entire UI frame based on the given context by
|
||||
/// ultimately flushing buffers down to the underlying terminal.
|
||||
///
|
||||
/// This function is executed by the UI thread whenever it receives a render message from the main
|
||||
/// thread.
|
||||
///
|
||||
/// It will draw the help bar, the results list, the input box, the preview content block, and the
|
||||
/// remote control.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `Result` containing the layout of the current frame if the drawing was successful.
|
||||
/// This layout can then be sent back to the main thread to serve for tasks where having that
|
||||
/// information can be useful or lead to optimizations.
|
||||
pub fn draw(ctx: &Ctx, f: &mut Frame<'_>, area: Rect) -> Result<Layout> {
|
||||
let selected_entry = ctx
|
||||
.tv_state
|
||||
.selected_entry
|
||||
.clone()
|
||||
.unwrap_or(ENTRY_PLACEHOLDER);
|
||||
|
||||
let show_preview = ctx.config.ui.show_preview_panel
|
||||
&& !matches!(selected_entry.preview_type, PreviewType::None);
|
||||
let show_preview =
|
||||
ctx.config.ui.show_preview_panel && ctx.tv_state.preview_state.enabled;
|
||||
let show_remote = !matches!(ctx.tv_state.mode, Mode::Channel);
|
||||
|
||||
let layout =
|
||||
@ -219,7 +239,7 @@ pub fn draw(ctx: &Ctx, f: &mut Frame<'_>, area: Rect) -> Result<Layout> {
|
||||
&ctx.colorscheme,
|
||||
)?;
|
||||
|
||||
if show_preview {
|
||||
if layout.preview_window.is_some() {
|
||||
draw_preview_content_block(
|
||||
f,
|
||||
layout.preview_window.unwrap(),
|
||||
|
@ -109,6 +109,7 @@ impl Preview {
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Hash)]
|
||||
pub struct PreviewState {
|
||||
pub enabled: bool,
|
||||
pub preview: Arc<Preview>,
|
||||
pub scroll: u16,
|
||||
pub target_line: Option<u16>,
|
||||
@ -118,11 +119,13 @@ const PREVIEW_MIN_SCROLL_LINES: u16 = 3;
|
||||
|
||||
impl PreviewState {
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
preview: Arc<Preview>,
|
||||
scroll: u16,
|
||||
target_line: Option<u16>,
|
||||
) -> Self {
|
||||
PreviewState {
|
||||
enabled,
|
||||
preview,
|
||||
scroll,
|
||||
target_line,
|
||||
|
@ -37,6 +37,11 @@ impl IoStream {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// The state of the UI after rendering.
|
||||
///
|
||||
/// This struct is returned by the UI thread to the main thread after each rendering cycle.
|
||||
/// It contains information that the main thread might be able to exploit to make certain
|
||||
/// decisions and optimizations.
|
||||
pub struct UiState {
|
||||
pub layout: Layout,
|
||||
}
|
||||
@ -47,6 +52,18 @@ impl UiState {
|
||||
}
|
||||
}
|
||||
|
||||
/// The main UI rendering task loop.
|
||||
///
|
||||
/// This function is responsible for rendering the UI based on the rendering tasks it receives from
|
||||
/// the main thread via `render_rx`.
|
||||
///
|
||||
/// This has a handle to the main action queue `action_tx` (for things like self-triggering
|
||||
/// subsequent rendering instructions) and the UI state queue `ui_state_tx` to send back the layout
|
||||
/// of the UI after each rendering cycle to the main thread to help make decisions and
|
||||
/// optimizations.
|
||||
///
|
||||
/// When starting the rendering loop, a choice is made to either render to stdout or stderr based
|
||||
/// on if the output is believed to be a TTY or not.
|
||||
pub async fn render(
|
||||
mut render_rx: mpsc::UnboundedReceiver<RenderingTask>,
|
||||
action_tx: mpsc::UnboundedSender<Action>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::action::Action;
|
||||
use crate::cable::load_cable_channels;
|
||||
use crate::channels::entry::{Entry, PreviewType, ENTRY_PLACEHOLDER};
|
||||
use crate::channels::entry::{Entry, ENTRY_PLACEHOLDER};
|
||||
use crate::channels::{
|
||||
remote_control::{load_builtin_channels, RemoteControl},
|
||||
OnAir, TelevisionChannel, UnitChannel,
|
||||
@ -9,7 +9,7 @@ use crate::config::{Config, Theme};
|
||||
use crate::draw::{ChannelState, Ctx, TvState};
|
||||
use crate::input::convert_action_to_input_request;
|
||||
use crate::picker::Picker;
|
||||
use crate::preview::{PreviewState, Previewer};
|
||||
use crate::preview::{Preview, PreviewState, Previewer};
|
||||
use crate::render::UiState;
|
||||
use crate::screen::colors::Colorscheme;
|
||||
use crate::screen::layout::InputPosition;
|
||||
@ -21,6 +21,7 @@ use anyhow::Result;
|
||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Hash, Eq, Debug, Serialize, Deserialize)]
|
||||
@ -79,6 +80,13 @@ impl Television {
|
||||
channel.find(&input.unwrap_or(EMPTY_STRING.to_string()));
|
||||
let spinner = Spinner::default();
|
||||
|
||||
let preview_state = PreviewState::new(
|
||||
channel.supports_preview(),
|
||||
Arc::new(Preview::default()),
|
||||
0,
|
||||
None,
|
||||
);
|
||||
|
||||
Self {
|
||||
action_tx,
|
||||
config,
|
||||
@ -91,7 +99,7 @@ impl Television {
|
||||
results_picker,
|
||||
rc_picker: Picker::default(),
|
||||
previewer,
|
||||
preview_state: PreviewState::default(),
|
||||
preview_state,
|
||||
spinner,
|
||||
spinner_state: SpinnerState::from(&spinner),
|
||||
app_metadata,
|
||||
@ -261,11 +269,13 @@ impl Television {
|
||||
}
|
||||
}
|
||||
|
||||
const RENDER_FIRST_N_TICKS: u64 = 20;
|
||||
const RENDER_EVERY_N_TICKS: u64 = 10;
|
||||
|
||||
impl Television {
|
||||
fn should_render(&self, action: &Action) -> bool {
|
||||
self.ticks == RENDER_EVERY_N_TICKS
|
||||
self.ticks < RENDER_FIRST_N_TICKS
|
||||
|| self.ticks % RENDER_EVERY_N_TICKS == 0
|
||||
|| matches!(
|
||||
action,
|
||||
Action::AddInputChar(_)
|
||||
@ -300,8 +310,7 @@ impl Television {
|
||||
&mut self,
|
||||
selected_entry: &Entry,
|
||||
) -> Result<()> {
|
||||
if self.config.ui.show_preview_panel
|
||||
&& !matches!(selected_entry.preview_type, PreviewType::None)
|
||||
if self.config.ui.show_preview_panel && self.channel.supports_preview()
|
||||
{
|
||||
// preview content
|
||||
if let Some(preview) = self
|
||||
@ -591,7 +600,6 @@ impl Television {
|
||||
if self.channel.running() {
|
||||
self.spinner.tick();
|
||||
}
|
||||
self.ticks = 0;
|
||||
|
||||
Some(Action::Render)
|
||||
} else {
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||
/// Global application metadata like version and current directory.
|
||||
pub struct AppMetadata {
|
||||
pub version: String,
|
||||
pub current_directory: String,
|
||||
|
Loading…
x
Reference in New Issue
Block a user