tlc: comprehensive W1-W5 fixes — dead dialogs, suspend, connection wiring, warning cleanup
W1: Fix 3 dead dialogs (DisplayBits/VfsSettings/LearnKeys) that could never close
- Capture handle_key() return value, close on Cancel/Confirm
W2: Implement Cmd::Suspend with actual SIGTSTP via kill -TSTP 4035172
- Add want_suspend field, ExternalAction::Suspend variant
- Drop TUI, send SIGTSTP, recreate TUI on resume
W3: Wire Connection dialog to Panel::navigate_to_vfs()
- Parse VFS URL, look up backend, redirect active panel
- Store Encoding dialog selection on FileManager.display_encoding
W4: Wire CK_EditUserMenu (EditorCmd::EditUserMenu)
- Opens user menu storage_path in editor via Editor::open()
- Fix unreachable!() in SaveBeforeClose prompt rendering
W5: Add ErrorOutcome::SkipAll variant + Shift-S keybinding
- Fix misleading doc comment about non-existent 'All' variants
- Add SkipAll button in error dialog render
Also: Fix all 41 compiler warnings (unused imports/vars, missing docs on
public API, remove dead SPECIAL_LABELS constant, remove unused viewer_bold)
1381 tests pass, zero warnings.
This commit is contained in:
@@ -352,9 +352,14 @@ fn render(tui: &mut Tui, fm: &mut FileManager) -> Result<()> {
|
||||
enum ExternalAction {
|
||||
Subshell(PathBuf),
|
||||
Command { cmd: String, cwd: PathBuf },
|
||||
Suspend,
|
||||
}
|
||||
|
||||
fn take_external_action(fm: &mut FileManager) -> Option<ExternalAction> {
|
||||
if fm.want_suspend {
|
||||
fm.want_suspend = false;
|
||||
return Some(ExternalAction::Suspend);
|
||||
}
|
||||
if fm.want_subshell {
|
||||
fm.want_subshell = false;
|
||||
return Some(ExternalAction::Subshell(
|
||||
@@ -379,14 +384,30 @@ fn run_external(
|
||||
) -> Result<Tui> {
|
||||
drop(tui);
|
||||
|
||||
if let Err(e) = match &action {
|
||||
ExternalAction::Subshell(cwd) => shell_manager.toggle_subshell(cwd),
|
||||
ExternalAction::Command { cmd, cwd } => {
|
||||
println!();
|
||||
shell_manager.run_command(cmd, cwd)
|
||||
match &action {
|
||||
ExternalAction::Suspend => {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let pid = std::process::id();
|
||||
let _ = std::process::Command::new("kill")
|
||||
.arg("-TSTP")
|
||||
.arg(pid.to_string())
|
||||
.status();
|
||||
}
|
||||
}
|
||||
ExternalAction::Subshell(cwd) => {
|
||||
if let Err(e) = shell_manager.toggle_subshell(cwd) {
|
||||
eprintln!("tlc: {e}");
|
||||
}
|
||||
}
|
||||
ExternalAction::Command { cmd, cwd } => {
|
||||
if let Err(e) = {
|
||||
println!();
|
||||
shell_manager.run_command(cmd, cwd)
|
||||
} {
|
||||
eprintln!("tlc: {e}");
|
||||
}
|
||||
}
|
||||
} {
|
||||
eprintln!("tlc: {e}");
|
||||
}
|
||||
|
||||
Tui::new()
|
||||
|
||||
@@ -77,6 +77,8 @@ pub enum EditorCmd {
|
||||
ToggleSyntax,
|
||||
/// Toggle relative line numbers (Alt-N).
|
||||
ToggleRelativeLines,
|
||||
/// Edit the user menu file (MC `CK_EditUserMenu`).
|
||||
EditUserMenu,
|
||||
}
|
||||
|
||||
/// Outcome of a menu-bar key press.
|
||||
|
||||
@@ -54,6 +54,7 @@ pub mod prompt;
|
||||
pub mod render;
|
||||
pub mod save;
|
||||
pub mod search;
|
||||
/// Spell checker with built-in dictionary.
|
||||
pub mod spell;
|
||||
#[cfg(feature = "syntect")]
|
||||
pub mod syntax;
|
||||
@@ -720,15 +721,18 @@ impl Editor {
|
||||
self.show_whitespace
|
||||
}
|
||||
|
||||
/// Toggle spell checking and return the new state.
|
||||
pub fn toggle_spell_check(&mut self) -> bool {
|
||||
self.spell_check_enabled = !self.spell_check_enabled;
|
||||
self.spell_check_enabled
|
||||
}
|
||||
|
||||
/// Whether spell checking is currently enabled.
|
||||
pub fn spell_check_enabled(&self) -> bool {
|
||||
self.spell_check_enabled
|
||||
}
|
||||
|
||||
/// Find the next misspelled word starting from `from_byte`.
|
||||
pub fn find_next_misspelled(&self, from_byte: usize) -> Option<(usize, usize)> {
|
||||
if !self.spell_check_enabled {
|
||||
return None;
|
||||
@@ -1014,14 +1018,17 @@ impl Editor {
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
/// Whether any secondary cursors are active.
|
||||
pub fn has_multi_cursor(&self) -> bool {
|
||||
!self.secondary_cursors.is_empty()
|
||||
}
|
||||
|
||||
/// Number of active secondary cursors.
|
||||
pub fn secondary_cursor_count(&self) -> usize {
|
||||
self.secondary_cursors.len()
|
||||
}
|
||||
|
||||
/// Add a secondary cursor at `byte_pos` (deduped, sorted).
|
||||
pub fn add_secondary_cursor(&mut self, byte_pos: usize) {
|
||||
let pos = byte_pos.min(self.buffer.len());
|
||||
if pos == self.cursor.position() {
|
||||
@@ -1034,10 +1041,12 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove all secondary cursors.
|
||||
pub fn clear_secondary_cursors(&mut self) {
|
||||
self.secondary_cursors.clear();
|
||||
}
|
||||
|
||||
/// Return all cursor positions (primary + secondary), sorted and deduped.
|
||||
pub fn all_cursor_positions(&self) -> Vec<usize> {
|
||||
let mut all = self.secondary_cursors.clone();
|
||||
all.push(self.cursor.position());
|
||||
@@ -1059,7 +1068,7 @@ impl Editor {
|
||||
let char_len = c.len_utf8();
|
||||
let line_before = self.buffer_line_of(self.cursor.position());
|
||||
let lines_before = self.buffer.line_count();
|
||||
let mut positions = self.all_cursor_positions();
|
||||
let positions = self.all_cursor_positions();
|
||||
for &pos in positions.iter().rev() {
|
||||
self.buffer.set_cursor(pos);
|
||||
self.buffer.insert_char(c);
|
||||
@@ -1085,7 +1094,7 @@ impl Editor {
|
||||
self.completer.cancel();
|
||||
let line_before = self.buffer_line_of(self.cursor.position());
|
||||
let lines_before = self.buffer.line_count();
|
||||
let mut positions = self.all_cursor_positions();
|
||||
let positions = self.all_cursor_positions();
|
||||
for &pos in positions.iter().rev() {
|
||||
if pos == 0 {
|
||||
continue;
|
||||
@@ -1371,6 +1380,13 @@ impl Editor {
|
||||
self.message = Some("No bookmarks set".to_string());
|
||||
}
|
||||
}
|
||||
EditorCmd::EditUserMenu => {
|
||||
let menu = crate::filemanager::usermenu::UserMenu::new();
|
||||
let path = menu.storage_path.clone();
|
||||
let new_editor = Self::open(&path);
|
||||
*self = new_editor;
|
||||
self.message = Some(format!("Editing user menu: {}", path.display()));
|
||||
}
|
||||
_ => {
|
||||
self.message = Some(format!("F9: {:?} (not yet wired)", cmd));
|
||||
}
|
||||
|
||||
@@ -512,7 +512,7 @@ impl Editor {
|
||||
PromptKind::BookmarkClear => crate::locale::t("dialog_title_bookmark_clear"),
|
||||
PromptKind::SaveAs => crate::locale::t("dialog_title_save_as"),
|
||||
PromptKind::InsertFile => "Insert File".to_string(),
|
||||
PromptKind::SaveBeforeClose => unreachable!(),
|
||||
PromptKind::SaveBeforeClose => "Save before close".to_string(),
|
||||
PromptKind::Sort => "Run sort".to_string(),
|
||||
};
|
||||
let label = match kind {
|
||||
@@ -525,7 +525,7 @@ impl Editor {
|
||||
| PromptKind::BookmarkClear => crate::locale::t("dialog_label_bookmark"),
|
||||
PromptKind::SaveAs => crate::locale::t("dialog_label_path"),
|
||||
PromptKind::InsertFile => crate::locale::t("dialog_label_path"),
|
||||
PromptKind::SaveBeforeClose => unreachable!(),
|
||||
PromptKind::SaveBeforeClose => "Save modified buffer?".to_string(),
|
||||
PromptKind::Sort => "sort options (empty for default)".to_string(),
|
||||
};
|
||||
let inner = render_popup(frame, popup, title, theme);
|
||||
|
||||
@@ -13,9 +13,11 @@ pub struct SpellChecker {
|
||||
words: HashSet<String>,
|
||||
}
|
||||
|
||||
/// Byte range (start, end) of a misspelled word.
|
||||
pub type Misspelled = (usize, usize);
|
||||
|
||||
impl SpellChecker {
|
||||
/// Create a spell checker populated with the built-in dictionary.
|
||||
pub fn new() -> Self {
|
||||
let words: HashSet<String> = BUILTIN_WORDS
|
||||
.iter()
|
||||
|
||||
@@ -12,10 +12,10 @@ use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::filemanager::{
|
||||
config_dialog, display_bits_dialog, edit_history, error_dialog, external_panelize,
|
||||
filtered_view, find, help, hotlist, layout_dialog, learn_keys_dialog, link,
|
||||
config_dialog, edit_history, error_dialog, external_panelize,
|
||||
filtered_view, find, help, hotlist, layout_dialog, link,
|
||||
overwrite_dialog, panel_options, percent, render, screen_list, skin_dialog, tree,
|
||||
usermenu, vfs_list, vfs_settings_dialog, DialogState, FileManager, LinkDialog,
|
||||
usermenu, vfs_list, DialogState, FileManager, LinkDialog,
|
||||
PendingErrorOp, PendingFileOp,
|
||||
};
|
||||
use crate::filemanager::link::LinkKind;
|
||||
@@ -941,7 +941,7 @@ impl FileManager {
|
||||
),
|
||||
)));
|
||||
} else {
|
||||
let handle = self.ops_manager.begin(
|
||||
let _handle = self.ops_manager.begin(
|
||||
crate::ops::OpKind::Copy,
|
||||
sources.clone(),
|
||||
Some(dst.clone()),
|
||||
@@ -1299,7 +1299,8 @@ impl FileManager {
|
||||
}
|
||||
}
|
||||
ErrorOutcome::Skip
|
||||
| ErrorOutcome::Ignore => {
|
||||
| ErrorOutcome::Ignore
|
||||
| ErrorOutcome::SkipAll => {
|
||||
self.status.set_message(format!("{}: skipped ({})", op.kind, d.failed_path));
|
||||
}
|
||||
ErrorOutcome::Abort => {
|
||||
|
||||
@@ -508,8 +508,7 @@ impl FileManager {
|
||||
}
|
||||
}
|
||||
Cmd::Suspend => {
|
||||
self.status
|
||||
.set_message("Suspend: use Ctrl-O to drop to a shell".to_string());
|
||||
self.want_suspend = true;
|
||||
Ok(true)
|
||||
}
|
||||
Cmd::PanelInfo => {
|
||||
@@ -713,18 +712,24 @@ impl FileManager {
|
||||
consumed = true;
|
||||
}
|
||||
Some(DialogState::DisplayBits(d)) => {
|
||||
d.handle_key(key);
|
||||
let r = d.handle_key(key);
|
||||
if r != display_bits_dialog::DialogOutcome::Running {
|
||||
self.dialog = None;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
Some(DialogState::VfsSettings(d)) => {
|
||||
d.handle_key(key);
|
||||
let r = d.handle_key(key);
|
||||
if r != vfs_settings_dialog::DialogOutcome::Running {
|
||||
self.dialog = None;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
Some(DialogState::LearnKeys(d)) => {
|
||||
// LearnKeys needs the keymap to resolve the captured
|
||||
// key to its Cmd. The dialog also consumes the key
|
||||
// so the filemanager does not also dispatch it.
|
||||
let _ = d.handle_key(key);
|
||||
let r = d.handle_key(key);
|
||||
if r == learn_keys_dialog::Outcome::Cancel {
|
||||
self.dialog = None;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
Some(DialogState::Layout(d)) => {
|
||||
@@ -799,6 +804,7 @@ impl FileManager {
|
||||
let r = d.handle_key(key);
|
||||
match r {
|
||||
encoding_dialog::EncodingResult::Select(enc) => {
|
||||
self.display_encoding = enc.clone();
|
||||
self.status.set_message(format!("Display encoding: {enc}"));
|
||||
self.dialog = None;
|
||||
}
|
||||
@@ -813,7 +819,23 @@ impl FileManager {
|
||||
let r = d.handle_key(key);
|
||||
match r {
|
||||
connection_dialog::ConnectionResult::Connect(url) => {
|
||||
self.status.set_message(format!("Connect: {url}"));
|
||||
match d.parse_vfs_path(&url) {
|
||||
Ok(vp) => {
|
||||
match self.active_panel_mut().navigate_to_vfs(vp) {
|
||||
Ok(()) => {
|
||||
self.status.set_message(format!("Connected: {url}"));
|
||||
}
|
||||
Err(e) => {
|
||||
self.status
|
||||
.set_message(format!("Connection failed: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.status
|
||||
.set_message(format!("Invalid URL: {e}"));
|
||||
}
|
||||
}
|
||||
self.dialog = None;
|
||||
}
|
||||
connection_dialog::ConnectionResult::Cancel => {
|
||||
|
||||
@@ -22,7 +22,7 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::key::Key;
|
||||
use crate::key::{Key, Modifiers};
|
||||
use crate::terminal::color::Theme;
|
||||
use crate::terminal::popup::{centered_cols_rect, render_popup};
|
||||
use crate::widget::button::{render_button_row, ButtonKind, ButtonSpec};
|
||||
@@ -30,11 +30,7 @@ use crate::widget::button::{render_button_row, ButtonKind, ButtonSpec};
|
||||
/// Outcome of the error recovery dialog.
|
||||
///
|
||||
/// Mirrors MC's `FileProgressStatus` enum from `filegui.h:45-54`
|
||||
/// (FILE_CONT, FILE_RETRY, FILE_SKIP, FILE_ABORT, FILE_IGNORE,
|
||||
/// FILE_IGNORE_ALL, FILE_SUSPEND). The "All" variants require a
|
||||
/// batch context which is not yet wired into the copy engine; they
|
||||
/// are accepted by the dialog but currently behave as their
|
||||
/// non-All counterparts.
|
||||
/// (FILE_CONT, FILE_RETRY, FILE_SKIP, FILE_ABORT, FILE_IGNORE).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ErrorOutcome {
|
||||
/// Continue with the next step (used internally as "no
|
||||
@@ -48,6 +44,8 @@ pub enum ErrorOutcome {
|
||||
Abort,
|
||||
/// Ignore this failure (alias for Skip; MC's FILE_IGNORE).
|
||||
Ignore,
|
||||
/// Skip all remaining failures in this batch without prompting.
|
||||
SkipAll,
|
||||
}
|
||||
|
||||
/// The error recovery dialog.
|
||||
@@ -83,6 +81,7 @@ impl ErrorDialog {
|
||||
/// Handle a key event. Returns true if the dialog consumed the
|
||||
/// key. Letter shortcuts follow MC's button labels:
|
||||
/// R = Retry, S = Skip, I = Ignore, A = Abort.
|
||||
/// Shift-S = Skip All (skip remaining errors in batch).
|
||||
/// Esc maps to Abort (matches MC).
|
||||
pub fn handle_key(&mut self, key: Key) -> bool {
|
||||
if key == Key::ESCAPE {
|
||||
@@ -90,6 +89,11 @@ impl ErrorDialog {
|
||||
self.finished = true;
|
||||
return true;
|
||||
}
|
||||
if key.mods.contains(Modifiers::SHIFT) && (key.code == b'S' as u32 || key.code == b's' as u32) {
|
||||
self.outcome = ErrorOutcome::SkipAll;
|
||||
self.finished = true;
|
||||
return true;
|
||||
}
|
||||
if key.mods.is_empty() {
|
||||
match key.code {
|
||||
c if c == b'r' as u32 || c == b'R' as u32 => {
|
||||
@@ -160,6 +164,11 @@ impl ErrorDialog {
|
||||
hotkey: Some('S'),
|
||||
kind: ButtonKind::Normal,
|
||||
},
|
||||
ButtonSpec {
|
||||
label: "SkipAll",
|
||||
hotkey: Some('S'),
|
||||
kind: ButtonKind::Normal,
|
||||
},
|
||||
ButtonSpec {
|
||||
label: "Ignore",
|
||||
hotkey: Some('I'),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//! The dialog closes on Esc.
|
||||
|
||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::style::Style;
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph};
|
||||
use ratatui::Frame;
|
||||
|
||||
@@ -19,8 +19,11 @@ use crate::terminal::popup::render_popup;
|
||||
/// Result of a menubar key press.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MenuBarOutcome {
|
||||
/// Menubar is still active, no action taken.
|
||||
Running,
|
||||
/// User selected a command; the optional usize is the panel index.
|
||||
Dispatch(Cmd, Option<usize>),
|
||||
/// User dismissed the menubar.
|
||||
Close,
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,9 @@ pub struct FileManager {
|
||||
/// The name of the active skin (used to write the config back
|
||||
/// when the user picks a different skin at runtime).
|
||||
pub skin_name: String,
|
||||
/// Selected display encoding (from Encoding dialog). Stored so
|
||||
/// the viewer and editor can consult it; currently informational.
|
||||
pub display_encoding: String,
|
||||
/// Active modal dialog (None = no dialog open).
|
||||
pub dialog: Option<DialogState>,
|
||||
/// Bottom-of-screen command line.
|
||||
@@ -181,6 +184,8 @@ pub struct FileManager {
|
||||
/// Set by dispatch when Ctrl-O (SubShell) is received. App checks
|
||||
/// this after dispatch to perform the suspend/spawn/resume cycle.
|
||||
pub want_subshell: bool,
|
||||
/// Set by dispatch when Ctrl-Z (Suspend) is received.
|
||||
pub want_suspend: bool,
|
||||
/// Set by `start_exec` when the user types a command in the
|
||||
/// command line. App checks this after dispatch and runs the
|
||||
/// command in the foreground terminal (not in a popup dialog).
|
||||
@@ -423,6 +428,7 @@ impl FileManager {
|
||||
ops_manager: crate::ops::OpsManager::new(),
|
||||
jobs: Arc::new(Mutex::new(jobs::JobRegistry::new())),
|
||||
skin_name,
|
||||
display_encoding: "UTF-8".to_string(),
|
||||
dialog: None,
|
||||
cmdline: cmdline::Cmdline::new(),
|
||||
editor: None,
|
||||
@@ -433,6 +439,7 @@ impl FileManager {
|
||||
menubar: None,
|
||||
panels_visible: true,
|
||||
want_subshell: false,
|
||||
want_suspend: false,
|
||||
want_exec: None,
|
||||
pending_op: None,
|
||||
pending_error_op: None,
|
||||
|
||||
@@ -732,6 +732,32 @@ impl Panel {
|
||||
self.read_directory(&resolved)
|
||||
}
|
||||
|
||||
/// Navigate the panel to a remote VFS path (FTP/SFTP/Shell link).
|
||||
/// Looks up the backend via [`crate::vfs::for_path`], stores it,
|
||||
/// and replaces the directory listing with the VFS root.
|
||||
pub fn navigate_to_vfs(&mut self, vp: VfsPath) -> Result<()> {
|
||||
match crate::vfs::for_path(&vp) {
|
||||
Ok(Some(backend)) => {
|
||||
self.vfs = Some(backend);
|
||||
self.vfs_path = Some(vp.clone());
|
||||
self.replace_directory_vfs(&vp)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
self.last_error = Some(
|
||||
"no VFS backend available for this scheme — \
|
||||
check that the feature is enabled"
|
||||
.to_string(),
|
||||
);
|
||||
Err(anyhow::anyhow!("no VFS backend for {vp}"))
|
||||
}
|
||||
Err(e) => {
|
||||
self.last_error = Some(format!("connection failed: {e}"));
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-read the current directory.
|
||||
pub fn refresh(&mut self) -> Result<()> {
|
||||
let p = self.path.clone();
|
||||
|
||||
@@ -81,8 +81,6 @@ pub const PERM_CELLS: [PermCell; 9] = [
|
||||
}, // other x
|
||||
];
|
||||
|
||||
const SPECIAL_LABELS: [&str; 3] = ["setuid", "setgid", "sticky"];
|
||||
|
||||
const SPECIAL_CELLS: [PermCell; 3] = [
|
||||
PermCell {
|
||||
class_shift: 0o4000,
|
||||
|
||||
@@ -193,11 +193,17 @@ pub enum Cmd {
|
||||
EditMenuFile,
|
||||
/// Command menu — edit the file highlighting rules.
|
||||
EditHighlightFile,
|
||||
/// File menu — view a file by path prompt.
|
||||
ViewFile,
|
||||
/// Options menu — display bits configuration.
|
||||
DisplayBits,
|
||||
/// Options menu — learn keys dialog.
|
||||
LearnKeysDialog,
|
||||
/// Options menu — virtual filesystem settings.
|
||||
VfsSettings,
|
||||
/// Command menu — browse command history.
|
||||
CommandHistory,
|
||||
/// Options menu — toggle confirmation dialogs.
|
||||
OptionsConfirm,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,16 @@ use crate::widget::ProgressGauge;
|
||||
/// The progress dialog. Renders a running operation with a
|
||||
/// centered modal overlay and a Cancel button.
|
||||
pub struct ProgressDialog {
|
||||
/// Dialog title shown in the border header.
|
||||
pub title: String,
|
||||
/// Handle into the ops manager for progress queries.
|
||||
pub handle: Option<OpHandle>,
|
||||
started: Option<Instant>,
|
||||
/// Width as percentage of screen (0.0–1.0).
|
||||
pub width_pct: f32,
|
||||
/// Height as percentage of screen (0.0–1.0).
|
||||
pub height_pct: f32,
|
||||
/// Whether the Cancel button has focus (vs the rest of the dialog).
|
||||
pub cancel_focused: bool,
|
||||
rate_samples: Vec<u64>,
|
||||
last_sample_at: Option<Instant>,
|
||||
|
||||
@@ -398,13 +398,29 @@ mod tests {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MouseAction {
|
||||
/// Left-click on a file item — select it.
|
||||
Click { row: u16, col: u16 },
|
||||
Click {
|
||||
/// Screen row of the click.
|
||||
row: u16,
|
||||
/// Screen column of the click.
|
||||
col: u16,
|
||||
},
|
||||
/// Double-click on a file item — enter the item.
|
||||
DoubleClick { row: u16, col: u16 },
|
||||
DoubleClick {
|
||||
/// Screen row of the click.
|
||||
row: u16,
|
||||
/// Screen column of the click.
|
||||
col: u16,
|
||||
},
|
||||
/// Scroll up by one row, or one page if `pages` is true.
|
||||
ScrollUp { pages: bool },
|
||||
ScrollUp {
|
||||
/// If true, scroll a full page instead of one row.
|
||||
pages: bool,
|
||||
},
|
||||
/// Scroll down by one row, or one page if `pages` is true.
|
||||
ScrollDown { pages: bool },
|
||||
ScrollDown {
|
||||
/// If true, scroll a full page instead of one row.
|
||||
pages: bool,
|
||||
},
|
||||
/// Click on the "<" history button (top of panel).
|
||||
HistoryPrev,
|
||||
/// Click on the ">" history button.
|
||||
|
||||
@@ -47,7 +47,6 @@ pub fn render(v: &mut Viewer, frame: &mut Frame, area: Rect, theme: &Theme) {
|
||||
}
|
||||
};
|
||||
let bytes = &viewport_bytes;
|
||||
let viewport_base = v.top * BYTES_PER_ROW;
|
||||
|
||||
let total_height = area.height as usize;
|
||||
if total_height == 0 {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Paragraph, Wrap};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::terminal::color::Theme;
|
||||
@@ -205,14 +205,12 @@ impl Default for TextView {
|
||||
/// follows the active skin.
|
||||
pub fn render(v: &mut Viewer, frame: &mut Frame, area: Rect, theme: &Theme) {
|
||||
let viewer_default = mc_skin::color_pair(theme.name, "viewer", "_default_");
|
||||
let viewer_bold = mc_skin::color_pair(theme.name, "viewer", "viewbold");
|
||||
let body_fg = viewer_default.map(|p| p.fg).unwrap_or(theme.foreground);
|
||||
let body_bg = viewer_default.map(|p| p.bg).unwrap_or(theme.background);
|
||||
let cursor_line_bg = match body_bg {
|
||||
Color::Rgb(r, g, b) => Color::Rgb(r.saturating_add(12), g.saturating_add(12), b.saturating_add(12)),
|
||||
_ => theme.current_line_bg(),
|
||||
};
|
||||
let bold_fg = viewer_bold.map(|p| p.fg).unwrap_or(theme.warning);
|
||||
#[cfg(feature = "syntect")]
|
||||
{
|
||||
if v.last_render_top != v.top {
|
||||
|
||||
@@ -117,7 +117,7 @@ impl ProgressGauge {
|
||||
/// Each cell represents 1/8 of a character width via the glyphs
|
||||
/// `▏▎▍▌▋▊▉█`, giving 8× horizontal resolution over a stock `Gauge`.
|
||||
/// The label is centered on top of the bar.
|
||||
pub fn render_fractional(&self, frame: &mut Frame, area: Rect, theme: &Theme) {
|
||||
pub fn render_fractional(&self, frame: &mut Frame, area: Rect, _theme: &Theme) {
|
||||
let width = area.width as usize;
|
||||
if width == 0 {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user