Log panel
This commit is contained in:
+15
-4
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::log::LogLine;
|
||||||
use color::Rgba8;
|
use color::Rgba8;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -35,7 +36,7 @@ impl Hash for Glyph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Glyph {
|
impl Glyph {
|
||||||
/// Returns the glyph used to render the player: tile 64 (`@`) in light blue on black.
|
/// Returns the glyph used to render the player: tile 64 (`@`) in white on dark blue.
|
||||||
///
|
///
|
||||||
/// This is the only hardcoded glyph; all other glyphs come from the map
|
/// This is the only hardcoded glyph; all other glyphs come from the map
|
||||||
/// file palette. It will be removed once the player becomes a scripted
|
/// file palette. It will be removed once the player becomes a scripted
|
||||||
@@ -43,8 +44,8 @@ impl Glyph {
|
|||||||
pub const fn player() -> Self {
|
pub const fn player() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tile: 64,
|
tile: 64,
|
||||||
fg: Rgba8 { r: 173, g: 216, b: 230, a: 255 }, // light blue
|
fg: Rgba8 { r: 255, g: 255, b: 255, a: 255 }, // white
|
||||||
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 }, // black
|
bg: Rgba8 { r: 0, g: 0, b: 200, a: 255 }, // dark blue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,12 +395,22 @@ impl Board {
|
|||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
/// The currently active board.
|
/// The currently active board.
|
||||||
pub board: Board,
|
pub board: Board,
|
||||||
|
/// The in-game message log, oldest first (newest pushed at the end).
|
||||||
|
pub log: Vec<LogLine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
/// Creates a `GameState` from a pre-loaded [`Board`].
|
/// Creates a `GameState` from a pre-loaded [`Board`].
|
||||||
pub fn new(board: Board) -> Self {
|
pub fn new(board: Board) -> Self {
|
||||||
Self { board }
|
Self {
|
||||||
|
board,
|
||||||
|
log: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends a styled message to the log.
|
||||||
|
pub fn log(&mut self, line: LogLine) {
|
||||||
|
self.log.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to move the player by `(dx, dy)` cells.
|
/// Attempts to move the player by `(dx, dy)` cells.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
/// Core game types: [`game::Board`], [`game::Glyph`], [`game::Archetype`], etc.
|
/// Core game types: [`game::Board`], [`game::Glyph`], [`game::Archetype`], etc.
|
||||||
pub mod game;
|
pub mod game;
|
||||||
|
/// Styled log messages ([`log::LogLine`]) for the in-game message feed.
|
||||||
|
pub mod log;
|
||||||
/// Map file loading and saving (`.toml` format).
|
/// Map file loading and saving (`.toml` format).
|
||||||
pub mod map_file;
|
pub mod map_file;
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
//! Styled log messages for the in-game message feed.
|
||||||
|
//!
|
||||||
|
//! A [`LogLine`] is one message in the game's log; it is a sequence of
|
||||||
|
//! [`LogSpan`]s so a single message can mix colors (e.g. a rainbow status
|
||||||
|
//! badge). Colors are stored as UI-agnostic [`Rgba8`] values — kiln-core does
|
||||||
|
//! not depend on any front-end — and each front-end converts a `LogLine` into
|
||||||
|
//! its own styled-text type at render time.
|
||||||
|
|
||||||
|
use color::Rgba8;
|
||||||
|
|
||||||
|
/// One run of text within a [`LogLine`], with optional foreground/background
|
||||||
|
/// colors. `None` for a color means "use the terminal/front-end default".
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct LogSpan {
|
||||||
|
/// The text of this run.
|
||||||
|
pub text: String,
|
||||||
|
/// Foreground color, or `None` to inherit the default.
|
||||||
|
pub fg: Option<Rgba8>,
|
||||||
|
/// Background color, or `None` to inherit the default.
|
||||||
|
pub bg: Option<Rgba8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single, possibly multi-colored log message: an ordered list of [`LogSpan`]s.
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct LogLine {
|
||||||
|
/// The styled runs that make up the message, in display order.
|
||||||
|
pub spans: Vec<LogSpan>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogLine {
|
||||||
|
/// Creates an empty line, to be filled with [`LogLine::push`].
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { spans: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a line with a single default-colored span.
|
||||||
|
pub fn raw(text: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
spans: vec![LogSpan {
|
||||||
|
text: text.into(),
|
||||||
|
fg: None,
|
||||||
|
bg: None,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends a styled span and returns `self`, so calls can be chained when
|
||||||
|
/// building a multi-color line.
|
||||||
|
pub fn push(mut self, text: impl Into<String>, fg: Option<Rgba8>, bg: Option<Rgba8>) -> Self {
|
||||||
|
self.spans.push(LogSpan {
|
||||||
|
text: text.into(),
|
||||||
|
fg,
|
||||||
|
bg,
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a whole other LogLine, all its spans, on to the end of this one.
|
||||||
|
pub fn append(mut self, mut other: LogLine) -> Self {
|
||||||
|
self.spans.append(&mut other.spans);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
+147
-35
@@ -12,14 +12,44 @@ mod render;
|
|||||||
mod term;
|
mod term;
|
||||||
|
|
||||||
use kiln_core::game::GameState;
|
use kiln_core::game::GameState;
|
||||||
|
use kiln_core::log::LogLine;
|
||||||
use kiln_core::map_file;
|
use kiln_core::map_file;
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind};
|
use ratatui::crossterm::event::{
|
||||||
use ratatui::widgets::Block;
|
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind,
|
||||||
use render::BoardWidget;
|
};
|
||||||
|
use ratatui::crossterm::execute;
|
||||||
|
use ratatui::layout::{Constraint, Layout, Spacing};
|
||||||
|
use ratatui::style::{Color, Style};
|
||||||
|
use ratatui::text::{Line, Span};
|
||||||
|
use ratatui::widgets::{Block, Paragraph, Wrap};
|
||||||
|
use render::{BoardWidget, logline_to_line};
|
||||||
|
use std::io;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
use ratatui::symbols::merge::MergeStrategy;
|
||||||
use term::TerminalCaps;
|
use term::TerminalCaps;
|
||||||
|
|
||||||
|
/// View-only UI state for the log panel. This is presentation state, so it lives
|
||||||
|
/// in the front-end rather than on the engine's [`GameState`].
|
||||||
|
struct Ui {
|
||||||
|
/// Whether the bottom log panel is open.
|
||||||
|
log_open: bool,
|
||||||
|
/// Height (in terminal rows, borders included) of the log panel when open.
|
||||||
|
log_height: u16,
|
||||||
|
/// Vertical scroll offset of the log panel, in lines from the top (newest).
|
||||||
|
scroll: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Ui {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
log_open: false,
|
||||||
|
log_height: 10,
|
||||||
|
scroll: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Entry point: parse the map path, load the board, then run the play loop with
|
/// Entry point: parse the map path, load the board, then run the play loop with
|
||||||
/// the terminal in raw/alternate-screen mode (restored on every exit path).
|
/// the terminal in raw/alternate-screen mode (restored on every exit path).
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
@@ -43,6 +73,10 @@ fn main() -> ExitCode {
|
|||||||
// `init` enters the alternate screen and raw mode; `restore` always undoes it.
|
// `init` enters the alternate screen and raw mode; `restore` always undoes it.
|
||||||
let mut terminal = ratatui::init();
|
let mut terminal = ratatui::init();
|
||||||
|
|
||||||
|
// Capture mouse events so the log panel can be scrolled with the wheel and
|
||||||
|
// resized by dragging its divider. Disabled again before restoring.
|
||||||
|
let _ = execute!(io::stdout(), EnableMouseCapture);
|
||||||
|
|
||||||
// Detect terminal capabilities (now that we're in raw mode) and enable the
|
// Detect terminal capabilities (now that we're in raw mode) and enable the
|
||||||
// Kitty keyboard protocol when it's available, so scripts can rely on the
|
// Kitty keyboard protocol when it's available, so scripts can rely on the
|
||||||
// richer bindings it provides. Disabled again before restoring the terminal.
|
// richer bindings it provides. Disabled again before restoring the terminal.
|
||||||
@@ -51,11 +85,19 @@ fn main() -> ExitCode {
|
|||||||
let _ = term::push_kitty_flags();
|
let _ = term::push_kitty_flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = run(&mut terminal, &mut game, &caps);
|
// Seed the log: the terminal-capabilities badge plus a couple of test
|
||||||
|
// messages (temporary, just to exercise the panel).
|
||||||
|
// Note these will appear in reverse order, latest on top
|
||||||
|
game.log(LogLine::raw("Press 'q' to exit."));
|
||||||
|
game.log(LogLine::raw("Welcome to kiln - ").append(caps.status_logline()));
|
||||||
|
|
||||||
|
let mut ui = Ui::default();
|
||||||
|
let result = run(&mut terminal, &mut game, &mut ui);
|
||||||
|
|
||||||
if caps.keyboard_enhancement {
|
if caps.keyboard_enhancement {
|
||||||
let _ = term::pop_kitty_flags();
|
let _ = term::pop_kitty_flags();
|
||||||
}
|
}
|
||||||
|
let _ = execute!(io::stdout(), DisableMouseCapture);
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
@@ -69,44 +111,114 @@ fn main() -> ExitCode {
|
|||||||
fn run(
|
fn run(
|
||||||
terminal: &mut ratatui::DefaultTerminal,
|
terminal: &mut ratatui::DefaultTerminal,
|
||||||
game: &mut GameState,
|
game: &mut GameState,
|
||||||
caps: &TerminalCaps,
|
ui: &mut Ui,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
// Redraw the board every iteration; ratatui diffs against the previous
|
// Redraw every iteration; ratatui diffs against the previous frame so
|
||||||
// frame so only changed cells are actually written to the terminal.
|
// only changed cells are actually written to the terminal.
|
||||||
terminal.draw(|frame| draw(frame, game, caps))?;
|
terminal.draw(|frame| draw(frame, game, ui))?;
|
||||||
|
|
||||||
// Block until the next input event.
|
match event::read()? {
|
||||||
if let Event::Key(key) = event::read()? {
|
Event::Key(key) => {
|
||||||
// Act on Press and Repeat so holding a key moves the player
|
// Act on Press and Repeat so holding a key moves the player
|
||||||
// continuously (the Kitty protocol emits Repeat events while a key is
|
// continuously (the Kitty protocol emits Repeat events while a
|
||||||
// held). Ignore Release, which that protocol also emits and which
|
// key is held). Ignore Release, which that protocol also emits
|
||||||
// would otherwise fire a second, spurious move per keypress.
|
// and which would otherwise fire a second, spurious move.
|
||||||
if key.kind == KeyEventKind::Release {
|
if key.kind == KeyEventKind::Release {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') | KeyCode::Esc => return Ok(()),
|
||||||
|
KeyCode::Up => game.try_move(0, -1),
|
||||||
|
KeyCode::Down => game.try_move(0, 1),
|
||||||
|
KeyCode::Left => game.try_move(-1, 0),
|
||||||
|
// Right arrow moves right; 'l' is reserved for the log panel.
|
||||||
|
KeyCode::Right => game.try_move(1, 0),
|
||||||
|
// 'l' toggles the log panel; reset the scroll to the newest.
|
||||||
|
KeyCode::Char('l') => {
|
||||||
|
ui.log_open = !ui.log_open;
|
||||||
|
ui.scroll = 0;
|
||||||
|
}
|
||||||
|
// PageUp/PageDown scroll the log when it's open.
|
||||||
|
KeyCode::PageUp if ui.log_open => scroll_log(ui, game, -5),
|
||||||
|
KeyCode::PageDown if ui.log_open => scroll_log(ui, game, 5),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match key.code {
|
// Wheel scrolls the open log; dragging the divider resizes it.
|
||||||
KeyCode::Char('q') | KeyCode::Esc => return Ok(()),
|
Event::Mouse(m) if ui.log_open => match m.kind {
|
||||||
KeyCode::Up | KeyCode::Char('k') => game.try_move(0, -1),
|
MouseEventKind::ScrollUp => scroll_log(ui, game, -1),
|
||||||
KeyCode::Down | KeyCode::Char('j') => game.try_move(0, 1),
|
MouseEventKind::ScrollDown => scroll_log(ui, game, 1),
|
||||||
KeyCode::Left | KeyCode::Char('h') => game.try_move(-1, 0),
|
MouseEventKind::Drag(_) => {
|
||||||
KeyCode::Right | KeyCode::Char('l') => game.try_move(1, 0),
|
// The divider sits at row `total - log_height`; dragging it up
|
||||||
|
// (smaller row) grows the panel. Keep both regions usable.
|
||||||
|
let total = terminal.size()?.height;
|
||||||
|
let target = total.saturating_sub(m.row);
|
||||||
|
ui.log_height = target.clamp(3, total.saturating_sub(3).max(3));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a single frame: a bordered block titled with the board name and
|
/// Adjusts the log scroll offset by `delta` lines, clamped so it can neither go
|
||||||
/// controls, containing the board itself.
|
/// above the newest message nor scroll past the oldest.
|
||||||
fn draw(frame: &mut Frame, game: &GameState, caps: &TerminalCaps) {
|
fn scroll_log(ui: &mut Ui, game: &GameState, delta: i32) {
|
||||||
let board = &game.board;
|
let max = game.log.len().saturating_sub(1) as i32;
|
||||||
let block = Block::bordered()
|
ui.scroll = (ui.scroll as i32 + delta).clamp(0, max) as u16;
|
||||||
.title(format!(" {} — arrows move · q quit ", board.name))
|
}
|
||||||
// Surface detected terminal capabilities in the bottom-right border.
|
|
||||||
.title_bottom(caps.summary_line().right_aligned());
|
/// Render a single frame: the board in a bordered block, and — when open — a log
|
||||||
// Draw the board inside the border, not over it.
|
/// panel across the bottom. When the panel is closed the latest log message is
|
||||||
let inner = block.inner(frame.area());
|
/// previewed in the board's bottom-left border after a `[l]og:` label.
|
||||||
frame.render_widget(block, frame.area());
|
fn draw(frame: &mut Frame, game: &GameState, ui: &Ui) {
|
||||||
frame.render_widget(BoardWidget::new(board), inner);
|
let board = &game.board;
|
||||||
|
|
||||||
|
if ui.log_open {
|
||||||
|
// Split the screen: board on top, log panel of `log_height` at the bottom.
|
||||||
|
let [board_area, log_area] =
|
||||||
|
Layout::vertical([Constraint::Min(3), Constraint::Length(ui.log_height)])
|
||||||
|
.spacing(Spacing::Overlap(1))
|
||||||
|
.areas(frame.area());
|
||||||
|
|
||||||
|
let block = Block::bordered().title(format!(" {} ", board.name)).merge_borders(MergeStrategy::Exact);
|
||||||
|
let inner = block.inner(board_area);
|
||||||
|
frame.render_widget(block, board_area);
|
||||||
|
frame.render_widget(BoardWidget::new(board), inner);
|
||||||
|
|
||||||
|
// Newest message on top: reverse the log into ratatui lines.
|
||||||
|
let log_block = Block::bordered()
|
||||||
|
.title(Span::styled(" [l]og: ", Style::default().fg(Color::DarkGray)))
|
||||||
|
.merge_borders(MergeStrategy::Exact);
|
||||||
|
let lines: Vec<Line> = game.log.iter().rev().map(logline_to_line).collect();
|
||||||
|
let paragraph = Paragraph::new(lines)
|
||||||
|
.block(log_block)
|
||||||
|
.scroll((ui.scroll, 0))
|
||||||
|
.wrap(Wrap { trim: false });
|
||||||
|
frame.render_widget(paragraph, log_area);
|
||||||
|
} else {
|
||||||
|
// Panel closed: board fills the screen, latest message in the border.
|
||||||
|
let mut block =
|
||||||
|
Block::bordered().title(format!(" {} ", board.name));
|
||||||
|
block = block.title_bottom(log_preview(game).left_aligned());
|
||||||
|
let inner = block.inner(frame.area());
|
||||||
|
frame.render_widget(block, frame.area());
|
||||||
|
frame.render_widget(BoardWidget::new(board), inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds the `[l]og: <latest message>` preview shown in the board's bottom-left
|
||||||
|
/// border when the log panel is closed.
|
||||||
|
fn log_preview(game: &GameState) -> Line<'static> {
|
||||||
|
let mut spans = vec![Span::styled(
|
||||||
|
" [l]og: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)];
|
||||||
|
if let Some(latest) = game.log.last() {
|
||||||
|
spans.extend(logline_to_line(latest).spans);
|
||||||
|
}
|
||||||
|
spans.push(Span::raw(" "));
|
||||||
|
Line::from(spans)
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-1
@@ -8,9 +8,11 @@
|
|||||||
use crate::cp437::tile_to_char;
|
use crate::cp437::tile_to_char;
|
||||||
use color::Rgba8;
|
use color::Rgba8;
|
||||||
use kiln_core::game::{Board, Glyph};
|
use kiln_core::game::{Board, Glyph};
|
||||||
|
use kiln_core::log::LogLine;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use ratatui::style::Color;
|
use ratatui::style::{Color, Style};
|
||||||
|
use ratatui::text::{Line, Span};
|
||||||
use ratatui::widgets::Widget;
|
use ratatui::widgets::Widget;
|
||||||
|
|
||||||
/// Converts a core [`Rgba8`] color into a ratatui truecolor [`Color`].
|
/// Converts a core [`Rgba8`] color into a ratatui truecolor [`Color`].
|
||||||
@@ -21,6 +23,29 @@ fn rgba8_to_color(c: Rgba8) -> Color {
|
|||||||
Color::Rgb(c.r, c.g, c.b)
|
Color::Rgb(c.r, c.g, c.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a core [`LogLine`] into a styled ratatui [`Line`] for display.
|
||||||
|
///
|
||||||
|
/// Each [`LogSpan`](kiln_core::log::LogSpan) becomes a ratatui [`Span`]; a span's
|
||||||
|
/// foreground/background color is applied only when present, so `None` colors
|
||||||
|
/// inherit the surrounding (default) style.
|
||||||
|
pub fn logline_to_line(line: &LogLine) -> Line<'static> {
|
||||||
|
let spans = line
|
||||||
|
.spans
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
let mut style = Style::default();
|
||||||
|
if let Some(fg) = s.fg {
|
||||||
|
style = style.fg(rgba8_to_color(fg));
|
||||||
|
}
|
||||||
|
if let Some(bg) = s.bg {
|
||||||
|
style = style.bg(rgba8_to_color(bg));
|
||||||
|
}
|
||||||
|
Span::styled(s.text.clone(), style)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Line::from(spans)
|
||||||
|
}
|
||||||
|
|
||||||
/// A ratatui widget that draws a [`Board`] (with objects and the player) into a
|
/// A ratatui widget that draws a [`Board`] (with objects and the player) into a
|
||||||
/// rectangular area, scrolled so the player stays visible on larger boards.
|
/// rectangular area, scrolled so the player stays visible on larger boards.
|
||||||
pub struct BoardWidget<'a> {
|
pub struct BoardWidget<'a> {
|
||||||
|
|||||||
+9
-13
@@ -9,10 +9,10 @@
|
|||||||
use ratatui::crossterm::event::{
|
use ratatui::crossterm::event::{
|
||||||
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
|
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
|
||||||
};
|
};
|
||||||
|
use color::Rgba8;
|
||||||
|
use kiln_core::log::LogLine;
|
||||||
use ratatui::crossterm::execute;
|
use ratatui::crossterm::execute;
|
||||||
use ratatui::crossterm::terminal::supports_keyboard_enhancement;
|
use ratatui::crossterm::terminal::supports_keyboard_enhancement;
|
||||||
use ratatui::style::{Color, Style};
|
|
||||||
use ratatui::text::{Line, Span};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
/// The input capabilities of the host terminal, detected once at startup.
|
/// The input capabilities of the host terminal, detected once at startup.
|
||||||
@@ -48,15 +48,16 @@ impl TerminalCaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A one-line styled summary for the UI, e.g. `"truecolor · enhanced input"`.
|
/// A one-line styled summary of terminal capabilities as a [`LogLine`],
|
||||||
|
/// e.g. `"truecolor · enhanced input"`, seeded into the game log at startup.
|
||||||
///
|
///
|
||||||
/// When truecolor is available the word "truecolor" is rendered with each
|
/// When truecolor is available the word "truecolor" is rendered with each
|
||||||
/// letter a different rainbow color — a fitting flex, since only a terminal
|
/// letter a different rainbow color — a fitting flex, since only a terminal
|
||||||
/// that can show per-cell RGB can display it. Otherwise the color word is the
|
/// that can show per-cell RGB can display it. Otherwise the color word is the
|
||||||
/// plain `"256-color"`. The input descriptor is always drawn in the default
|
/// plain `"256-color"`. The input descriptor is always drawn in the default
|
||||||
/// color.
|
/// color.
|
||||||
pub fn summary_line(&self) -> Line<'static> {
|
pub fn status_logline(&self) -> LogLine {
|
||||||
let mut spans: Vec<Span<'static>> = vec![Span::raw(" ")];
|
let mut line = LogLine::new();
|
||||||
|
|
||||||
if self.truecolor {
|
if self.truecolor {
|
||||||
// Rotate the hue across the letters so each gets a distinct spectrum color.
|
// Rotate the hue across the letters so each gets a distinct spectrum color.
|
||||||
@@ -64,13 +65,10 @@ impl TerminalCaps {
|
|||||||
let n = word.chars().count();
|
let n = word.chars().count();
|
||||||
for (i, ch) in word.chars().enumerate() {
|
for (i, ch) in word.chars().enumerate() {
|
||||||
let (r, g, b) = hue_to_rgb(i as f32 / n as f32 * 360.0);
|
let (r, g, b) = hue_to_rgb(i as f32 / n as f32 * 360.0);
|
||||||
spans.push(Span::styled(
|
line = line.push(ch.to_string(), Some(Rgba8 { r, g, b, a: 255 }), None);
|
||||||
ch.to_string(),
|
|
||||||
Style::default().fg(Color::Rgb(r, g, b)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spans.push(Span::raw("256-color"));
|
line = line.push("256-color", None, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let input = if self.keyboard_enhancement {
|
let input = if self.keyboard_enhancement {
|
||||||
@@ -78,9 +76,7 @@ impl TerminalCaps {
|
|||||||
} else {
|
} else {
|
||||||
"legacy input"
|
"legacy input"
|
||||||
};
|
};
|
||||||
spans.push(Span::raw(format!(" · {input} ")));
|
line.push(format!(" · {input}"), None, None)
|
||||||
|
|
||||||
Line::from(spans)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user