Polish window frames: inset title chip, ✕ close button, drop shadow
- Title rendered as a chip inset in the border (┤ titel ├), accent colored when focused, dimmed when not - Close button restyled to ┤✕├ matching the frame - Subtle drop shadow below/right of windows (darkened cells, content underneath stays visible; skipped when a background image is active) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,12 @@ fn render_window(
|
|||||||
WindowContent::PopupDialog { .. } => " TUI-WM ".to_string(),
|
WindowContent::PopupDialog { .. } => " TUI-WM ".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Diskret skugga nedanför/till höger om fönstret (inte när en
|
||||||
|
// bakgrundsbild används — då ska skrivbordet synas igenom).
|
||||||
|
if outer_bg != Color::Reset {
|
||||||
|
render_shadow(frame, full_rect);
|
||||||
|
}
|
||||||
|
|
||||||
frame.render_widget(Clear, full_rect);
|
frame.render_widget(Clear, full_rect);
|
||||||
// Ramzonen målas i skrivbordets färg — annars fylls även hörncellerna
|
// Ramzonen målas i skrivbordets färg — annars fylls även hörncellerna
|
||||||
// utanför de rundade hörnen med fönstrets bakgrund och rutan ser
|
// utanför de rundade hörnen med fönstrets bakgrund och rutan ser
|
||||||
@@ -233,16 +239,22 @@ fn render_window(
|
|||||||
Block::default().style(Style::default().bg(content_bg)),
|
Block::default().style(Style::default().bg(content_bg)),
|
||||||
inner_rect,
|
inner_rect,
|
||||||
);
|
);
|
||||||
|
// Titeln renderas som ett "chip" infällt i ramen: ┤ titel ├
|
||||||
|
let title_line = Line::from(vec![
|
||||||
|
Span::styled("┤", border_style),
|
||||||
|
Span::styled(title_text.clone(), theme::window_title(is_focused)),
|
||||||
|
Span::styled("├", border_style),
|
||||||
|
]);
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(Span::styled(&title_text, theme::window_title()))
|
.title(title_line)
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Rounded)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(border_style),
|
.border_style(border_style),
|
||||||
full_rect,
|
full_rect,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stäng-knapp [X]
|
// Stäng-knapp: ┤✕├ infälld i ramen (✕ U+2715 är ett smalt textglyf)
|
||||||
let cb = window.close_btn_rect();
|
let cb = window.close_btn_rect();
|
||||||
if cb.x + 3 <= full_rect.x + full_rect.width {
|
if cb.x + 3 <= full_rect.x + full_rect.width {
|
||||||
let close_hov = hovered_close == Some(window.id);
|
let close_hov = hovered_close == Some(window.id);
|
||||||
@@ -251,7 +263,12 @@ fn render_window(
|
|||||||
} else {
|
} else {
|
||||||
Style::default().fg(theme::DANGER)
|
Style::default().fg(theme::DANGER)
|
||||||
};
|
};
|
||||||
frame.render_widget(Paragraph::new(Span::styled("[X]", close_style)), cb);
|
let close_line = Line::from(vec![
|
||||||
|
Span::styled("┤", border_style),
|
||||||
|
Span::styled("✕", close_style),
|
||||||
|
Span::styled("├", border_style),
|
||||||
|
]);
|
||||||
|
frame.render_widget(Paragraph::new(close_line), cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize-hints – bara om fönstret är resizable
|
// Resize-hints – bara om fönstret är resizable
|
||||||
@@ -309,6 +326,31 @@ fn render_window(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ritar en skugga: raden under och kolumnen till höger om fönstret får
|
||||||
|
/// mörkare bakgrund + dämpad text, så fönstren ser ut att sväva över
|
||||||
|
/// skrivbordet. Muterar cellerna direkt så att underliggande innehåll
|
||||||
|
/// (andra fönster/skrivbordet) syns kvar — bara mörkare.
|
||||||
|
fn render_shadow(frame: &mut Frame, rect: Rect) {
|
||||||
|
let area = frame.area();
|
||||||
|
let buf = frame.buffer_mut();
|
||||||
|
let mut shade = |x: u16, y: u16| {
|
||||||
|
if x < area.width && y < area.height {
|
||||||
|
if let Some(cell) = buf.cell_mut((x, y)) {
|
||||||
|
cell.set_bg(theme::SHADOW_BG);
|
||||||
|
cell.modifier.insert(Modifier::DIM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Rad under fönstret (förskjuten ett steg åt höger)
|
||||||
|
for x in (rect.x + 1)..(rect.x + rect.width + 1) {
|
||||||
|
shade(x, rect.y + rect.height);
|
||||||
|
}
|
||||||
|
// Kolumn till höger om fönstret
|
||||||
|
for y in (rect.y + 1)..(rect.y + rect.height) {
|
||||||
|
shade(rect.x + rect.width, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_popup_dialog(
|
fn render_popup_dialog(
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
window: &FloatingWindow,
|
window: &FloatingWindow,
|
||||||
|
|||||||
13
src/theme.rs
13
src/theme.rs
@@ -64,7 +64,14 @@ pub fn panel_item() -> Style {
|
|||||||
Style::default().fg(PANEL_TEXT).bg(PANEL_BG)
|
Style::default().fg(PANEL_TEXT).bg(PANEL_BG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fönstertitel.
|
/// Fönstertitel — accentfärgad när fönstret har fokus.
|
||||||
pub fn window_title() -> Style {
|
pub fn window_title(focused: bool) -> Style {
|
||||||
Style::default().fg(TEXT).add_modifier(Modifier::BOLD)
|
if focused {
|
||||||
|
Style::default().fg(ACCENT).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(HINT)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bakgrundsfärg för fönsterskuggan (mörkare än skrivbordet).
|
||||||
|
pub const SHADOW_BG: Color = Color::Indexed(233);
|
||||||
|
|||||||
Reference in New Issue
Block a user