1
0
Fork 0
forked from wry/wry

all: remove traditional i3 titlebars, add corner rounding

This commit is contained in:
kossLAN 2026-04-09 23:04:33 -04:00
parent e1928863d9
commit a41dbae899
No known key found for this signature in database
52 changed files with 1866 additions and 1047 deletions

View file

@ -2,7 +2,6 @@ use {
crate::{
cmm::cmm_render_intent::RenderIntent,
gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
icons::{IconState, SizedIcons},
ifs::wl_surface::{
SurfaceBuffer, WlSurface,
x_surface::xwindow::Xwindow,
@ -13,7 +12,7 @@ use {
renderer::renderer_base::RendererBase,
scale::Scale,
state::State,
theme::Color,
theme::{Color, CornerRadius},
tree::{
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
ToplevelNodeBase, WorkspaceNode,
@ -29,8 +28,8 @@ pub struct Renderer<'a> {
pub state: &'a State,
pub logical_extents: Rect,
pub pixel_extents: Rect,
pub icons: Option<Rc<SizedIcons>>,
pub stretch: Option<(i32, i32)>,
pub corner_radius: Option<CornerRadius>,
}
impl Renderer<'_> {
@ -283,60 +282,12 @@ impl Renderer<'_> {
let srgb = &srgb_srgb.linear;
let perceptual = RenderIntent::Perceptual;
let rd = container.render_data.borrow_mut();
let c = self.state.theme.colors.unfocused_title_background.get();
self.base
.fill_boxes2(&rd.title_rects, &c, srgb, perceptual, x, y);
let c = self.state.theme.colors.focused_title_background.get();
self.base
.fill_boxes2(&rd.active_title_rects, &c, srgb, perceptual, x, y);
let c = self.state.theme.colors.attention_requested_background.get();
self.base
.fill_boxes2(&rd.attention_title_rects, &c, srgb, perceptual, x, y);
let c = self.state.theme.colors.separator.get();
self.base
.fill_boxes2(&rd.underline_rects, &c, srgb, perceptual, x, y);
let c = self.state.theme.colors.border.get();
self.base
.fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y);
if let Some(lar) = &rd.last_active_rect {
let c = self
.state
.theme
.colors
.focused_inactive_title_background
.get();
self.base
.fill_boxes2(std::slice::from_ref(lar), &c, srgb, perceptual, x, y);
}
if let Some(titles) = rd.titles.get(&self.base.scale) {
for title in titles {
let rect = title.rect.move_(x, y);
let bounds = self.base.scale_rect(rect);
let (x, y) = self.base.scale_point(rect.x1(), rect.y1());
self.base.render_texture(
&title.tex,
None,
x,
y,
None,
None,
self.base.scale,
Some(&bounds),
None,
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
perceptual,
AlphaMode::PremultipliedElectrical,
);
}
}
}
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
let floating = self.state.theme.floating_titles.get();
self.render_container_decorations(container, x, y);
if let Some(child) = container.mono_child.get() {
@ -355,44 +306,36 @@ impl Renderer<'_> {
let full_w = mb.width();
let srgb = &srgb_srgb.linear;
let perceptual = RenderIntent::Perceptual;
if floating {
if !child.node.node_is_container() {
let body_frame = [
Rect::new_sized_saturating(mb.x1() - bw, mb.y1(), bw, mb.y2() - mb.y1()),
Rect::new_sized_saturating(mb.x2(), mb.y1(), bw, mb.y2() - mb.y1()),
Rect::new_sized_saturating(mb.x1() - bw, mb.y1() - bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(mb.x1() - bw, mb.y2(), full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&body_frame, c, srgb, perceptual, x, y);
}
let th = self.state.theme.title_height();
if th > 0 {
for tab in container.children.iter() {
let tr = tab.title_rect.get();
let tc = if tab.active.get() {
&focused_border_color
} else {
&border_color
};
let tw = tr.width();
let tab_frame = [
Rect::new_sized_saturating(tr.x1() - bw, tr.y1() - bw, tw + 2 * bw, bw),
Rect::new_sized_saturating(tr.x1() - bw, tr.y1(), bw, th),
Rect::new_sized_saturating(tr.x2(), tr.y1(), bw, th),
Rect::new_sized_saturating(tr.x1() - bw, tr.y1() + th, tw + 2 * bw, bw),
];
self.base.fill_boxes2(&tab_frame, tc, srgb, perceptual, x, y);
}
}
} else if !child.node.node_is_container() {
if !child.node.node_is_container() {
let cr = self.state.theme.corner_radius.get();
let full_h = mb.y2();
let frame_rects = [
Rect::new_sized_saturating(mb.x1() - bw, 0, bw, full_h),
Rect::new_sized_saturating(mb.x2(), 0, bw, full_h),
Rect::new_sized_saturating(mb.x1() - bw, -bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(mb.x1() - bw, full_h, full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
if cr.is_zero() {
let frame_rects = [
Rect::new_sized_saturating(mb.x1() - bw, 0, bw, full_h),
Rect::new_sized_saturating(mb.x2(), 0, bw, full_h),
Rect::new_sized_saturating(mb.x1() - bw, -bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(mb.x1() - bw, full_h, full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
} else {
let outer = Rect::new_sized_saturating(
mb.x1() - bw,
-bw,
full_w + 2 * bw,
full_h + 2 * bw,
);
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer.move_(x, y),
c,
None,
srgb,
perceptual,
scaled_cr,
bw as f32 * scalef,
);
}
}
}
let body = mb.move_(x, y);
@ -403,28 +346,36 @@ impl Renderer<'_> {
} else {
None
};
if self.state.theme.sizes.gap.get() != 0 && !child.node.node_is_container() {
let cr = self.state.theme.corner_radius.get();
if !cr.is_zero() {
let scalef = self.base.scalef as f32;
let bw = self.state.theme.sizes.border_width.get();
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
self.corner_radius = Some(inner_cr);
}
}
child
.node
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
self.stretch = None;
} else {
let gap = self.state.theme.sizes.gap.get();
let (srgb_srgb, bw, border_color, focused_border_color, tpuh) = if gap != 0 {
let (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 {
let srgb_srgb = self.state.color_manager.srgb_gamma22();
let bw = self.state.theme.sizes.border_width.get();
let border_color = self.state.theme.colors.border.get();
let focused_border_color = self.state.theme.colors.focused_title_background.get();
let tpuh = self.state.theme.title_plus_underline_height();
(
Some(srgb_srgb),
bw,
border_color,
focused_border_color,
tpuh,
)
} else {
(None, 0, Color::SOLID_BLACK, Color::SOLID_BLACK, 0)
(None, 0, Color::SOLID_BLACK, Color::SOLID_BLACK)
};
let cr = self.state.theme.corner_radius.get();
for child in container.children.iter() {
let body = child.body.get();
if body.x1() >= container.width.get() || body.y1() >= container.height.get() {
@ -432,45 +383,42 @@ impl Renderer<'_> {
}
if let Some(srgb_srgb) = srgb_srgb {
let srgb = &srgb_srgb.linear;
let c = if floating {
if child.active.get() { &focused_border_color } else { &border_color }
} else if child.border_color_is_focused.get() {
let c = if child.border_color_is_focused.get() {
&focused_border_color
} else {
&border_color
};
let title_rect = child.title_rect.get();
let full_w = body.width();
let perceptual = RenderIntent::Perceptual;
if floating && tpuh > 0 {
let tw = title_rect.width();
let title_h = title_rect.height();
let title_frame = [
Rect::new_sized_saturating(title_rect.x1() - bw, title_rect.y1() - bw, tw + 2 * bw, bw),
Rect::new_sized_saturating(title_rect.x1() - bw, title_rect.y1(), bw, title_h),
Rect::new_sized_saturating(title_rect.x2(), title_rect.y1(), bw, title_h),
Rect::new_sized_saturating(title_rect.x1() - bw, title_rect.y1() + title_h, tw + 2 * bw, bw),
];
self.base.fill_boxes2(&title_frame, c, srgb, perceptual, x, y);
if !child.node.node_is_container() && gap != 0 {
let body_frame = [
Rect::new_sized_saturating(body.x1() - bw, body.y1(), bw, body.y2() - body.y1()),
Rect::new_sized_saturating(body.x2(), body.y1(), bw, body.y2() - body.y1()),
if !child.node.node_is_container() && gap != 0 {
let full_w = body.width();
let perceptual = RenderIntent::Perceptual;
let full_h = body.height();
if cr.is_zero() {
let frame_rects = [
Rect::new_sized_saturating(body.x1() - bw, body.y1(), bw, full_h),
Rect::new_sized_saturating(body.x2(), body.y1(), bw, full_h),
Rect::new_sized_saturating(body.x1() - bw, body.y1() - bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(body.x1() - bw, body.y2(), full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&body_frame, c, srgb, perceptual, x, y);
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
} else {
let outer = Rect::new_sized_saturating(
body.x1() - bw,
body.y1() - bw,
full_w + 2 * bw,
full_h + 2 * bw,
);
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer.move_(x, y),
c,
None,
srgb,
perceptual,
scaled_cr,
bw as f32 * scalef,
);
}
} else if !child.node.node_is_container() && gap != 0 {
let top_y = if tpuh > 0 { title_rect.y1() } else { body.y1() };
let full_h = body.y2() - top_y;
let frame_rects = [
Rect::new_sized_saturating(body.x1() - bw, top_y, bw, full_h),
Rect::new_sized_saturating(body.x2(), top_y, bw, full_h),
Rect::new_sized_saturating(body.x1() - bw, top_y - bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(body.x1() - bw, body.y2(), full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
}
}
let content = child.content.get();
@ -479,6 +427,11 @@ impl Renderer<'_> {
} else {
None
};
if !cr.is_zero() && !child.node.node_is_container() && gap != 0 {
let scalef = self.base.scalef as f32;
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
self.corner_radius = Some(inner_cr);
}
let body = body.move_(x, y);
let body = self.base.scale_rect(body);
child
@ -642,28 +595,49 @@ impl Renderer<'_> {
let cd = surface.color_description();
let intent = surface.render_intent();
let alpha_mode = surface.alpha_mode();
let corner_radius = self.corner_radius.take();
if let Some(tex) = buf.get_texture(surface) {
let mut opaque = surface.opaque();
if !opaque && tex.format().has_alpha {
opaque = self.bounds_are_opaque(x, y, bounds, surface);
if let Some(cr) = corner_radius {
self.base.render_rounded_texture(
&tex,
alpha,
x,
y,
Some(tpoints),
Some(tsize),
self.base.scale,
bounds,
Some(buffer.clone()),
AcquireSync::Unnecessary,
buffer.release_sync,
&cd,
intent,
alpha_mode,
cr,
);
} else {
let mut opaque = surface.opaque();
if !opaque && tex.format().has_alpha {
opaque = self.bounds_are_opaque(x, y, bounds, surface);
}
self.base.render_texture(
&tex,
alpha,
x,
y,
Some(tpoints),
Some(tsize),
self.base.scale,
bounds,
Some(buffer.clone()),
AcquireSync::Unnecessary,
buffer.release_sync,
opaque,
&cd,
intent,
alpha_mode,
);
}
self.base.render_texture(
&tex,
alpha,
x,
y,
Some(tpoints),
Some(tsize),
self.base.scale,
bounds,
Some(buffer.clone()),
AcquireSync::Unnecessary,
buffer.release_sync,
opaque,
&cd,
intent,
alpha_mode,
);
} else if let Some(color) = &buf.color {
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
let rect = match bounds {
@ -691,128 +665,60 @@ impl Renderer<'_> {
};
let pos = floating.position.get();
let theme = &self.state.theme;
let th = theme.title_height();
let tpuh = theme.title_plus_underline_height();
let tuh = theme.title_underline_height();
let bw = theme.sizes.border_width.get();
let bc = if floating.active.get() {
theme.colors.focused_title_background.get()
} else {
theme.colors.border.get()
};
let tc = if floating.active.get() {
theme.colors.focused_title_background.get()
} else if floating.attention_requested.get() {
theme.colors.attention_requested_background.get()
} else {
theme.colors.unfocused_title_background.get()
};
let uc = theme.colors.separator.get();
let gap = theme.sizes.gap.get();
let floating_title = theme.floating_titles.get() && gap != 0;
let srgb_srgb = self.state.color_manager.srgb_gamma22();
let srgb = &srgb_srgb.linear;
let perceptual = RenderIntent::Perceptual;
if floating_title {
let title_frame = [
Rect::new_sized_saturating(x, y, pos.width(), bw),
Rect::new_sized_saturating(x, y + bw, bw, th),
Rect::new_sized_saturating(x + pos.width() - bw, y + bw, bw, th),
Rect::new_sized_saturating(x, y + bw + th, pos.width(), bw),
];
self.base.fill_boxes(&title_frame, &bc, srgb, perceptual);
let title_bg = [Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, th)];
self.base.fill_boxes(&title_bg, &tc, srgb, perceptual);
let body_top = y + tpuh;
let body_inner_top = y + bw + tpuh;
let body_inner_height = pos.height() - 2 * bw - tpuh;
let body_frame = [
Rect::new_sized_saturating(x, body_top, pos.width(), bw),
Rect::new_sized_saturating(x, body_inner_top, bw, body_inner_height),
Rect::new_sized_saturating(x + pos.width() - bw, body_inner_top, bw, body_inner_height),
Rect::new_sized_saturating(x, y + pos.height() - bw, pos.width(), bw),
];
self.base.fill_boxes(&body_frame, &bc, srgb, perceptual);
} else {
let cr = theme.corner_radius.get();
if cr.is_zero() {
let borders = [
Rect::new_sized_saturating(x, y, pos.width(), bw),
Rect::new_sized_saturating(x, y + bw, bw, pos.height() - bw),
Rect::new_sized_saturating(x + pos.width() - bw, y + bw, bw, pos.height() - bw),
Rect::new_sized_saturating(x + bw, y + pos.height() - bw, pos.width() - 2 * bw, bw),
Rect::new_sized_saturating(
x + pos.width() - bw,
y + bw,
bw,
pos.height() - bw,
),
Rect::new_sized_saturating(
x + bw,
y + pos.height() - bw,
pos.width() - 2 * bw,
bw,
),
];
self.base.fill_boxes(&borders, &bc, srgb, perceptual);
let title = [Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, th)];
self.base.fill_boxes(&title, &tc, srgb, perceptual);
let title_underline = [Rect::new_sized_saturating(x + bw, y + bw + th, pos.width() - 2 * bw, tuh)];
self.base.fill_boxes(&title_underline, &uc, srgb, perceptual);
}
let rect = floating.title_rect.get().move_(x, y);
let bounds = self.base.scale_rect(rect);
let (mut x1, y1) = rect.position();
let is_pinned = floating.pinned_link.borrow().is_some();
if is_pinned || self.state.show_pin_icon.get() {
let (x, y) = self.base.scale_point(x1, y1);
if let Some(icons) = &self.icons {
let icon = if floating.active.get() {
&icons.pin_focused_title
} else if floating.attention_requested.get() {
&icons.pin_attention_requested
} else {
&icons.pin_unfocused_title
};
let state = match is_pinned {
true => IconState::Active,
false => IconState::Passive,
};
self.base.render_texture(
&icon[state],
None,
x,
y,
None,
None,
self.base.scale,
Some(&bounds),
None,
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
perceptual,
AlphaMode::PremultipliedElectrical,
);
}
x1 += th;
}
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale)
&& let Some(texture) = title.texture()
{
let (x, y) = self.base.scale_point(x1, y1);
self.base.render_texture(
&texture,
} else {
let outer = Rect::new_sized_saturating(x, y, pos.width(), pos.height());
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer,
&bc,
None,
x,
y,
None,
None,
self.base.scale,
Some(&bounds),
None,
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
srgb,
perceptual,
AlphaMode::PremultipliedElectrical,
scaled_cr,
bw as f32 * scalef,
);
}
let body = Rect::new_sized_saturating(
x + bw,
y + bw + tpuh,
y + bw,
pos.width() - 2 * bw,
pos.height() - 2 * bw - tpuh,
pos.height() - 2 * bw,
);
let scissor_body = self.base.scale_rect(body);
if !cr.is_zero() {
let scalef = self.base.scalef as f32;
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
self.corner_radius = Some(inner_cr);
}
child.node_render(self, body.x1(), body.y1(), Some(&scissor_body));
}