all: remove traditional i3 titlebars, add corner rounding
This commit is contained in:
parent
e1928863d9
commit
a41dbae899
52 changed files with 1866 additions and 1047 deletions
|
|
@ -20,10 +20,18 @@ pub const TREES: &[Tree] = &[
|
|||
"tex.frag",
|
||||
"out.vert",
|
||||
"out.frag",
|
||||
"rounded_fill.frag",
|
||||
"rounded_fill.vert",
|
||||
"rounded_tex.frag",
|
||||
"rounded_tex.vert",
|
||||
"legacy/fill.frag",
|
||||
"legacy/fill.vert",
|
||||
"legacy/tex.vert",
|
||||
"legacy/tex.frag",
|
||||
"legacy/rounded_fill.frag",
|
||||
"legacy/rounded_fill.vert",
|
||||
"legacy/rounded_tex.frag",
|
||||
"legacy/rounded_tex.vert",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2025,6 +2025,16 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetPointerRevertKey { seat, key });
|
||||
}
|
||||
|
||||
pub fn set_corner_radius(&self, radius: f32) {
|
||||
self.send(&ClientMessage::SetCornerRadius { radius });
|
||||
}
|
||||
|
||||
pub fn get_corner_radius(&self) -> f32 {
|
||||
let res = self.send_with_response(&ClientMessage::GetCornerRadius);
|
||||
get_response!(res, 0.0, GetCornerRadius { radius });
|
||||
radius
|
||||
}
|
||||
|
||||
fn handle_msg(&self, msg: &[u8]) {
|
||||
self.handle_msg2(msg);
|
||||
self.dispatch_futures();
|
||||
|
|
|
|||
|
|
@ -880,6 +880,10 @@ pub enum ClientMessage<'a> {
|
|||
seat: Seat,
|
||||
enabled: bool,
|
||||
},
|
||||
SetCornerRadius {
|
||||
radius: f32,
|
||||
},
|
||||
GetCornerRadius,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
@ -1136,6 +1140,9 @@ pub enum Response {
|
|||
ConnectorSupportsArbitraryModes {
|
||||
supports_arbitrary_modes: bool,
|
||||
},
|
||||
GetCornerRadius {
|
||||
radius: f32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -382,6 +382,18 @@ pub fn toggle_floating_titles() {
|
|||
get.set_floating_titles(!get.get_floating_titles());
|
||||
}
|
||||
|
||||
/// Sets the corner radius for window borders.
|
||||
///
|
||||
/// A radius of 0 means square corners. The radius is in logical pixels.
|
||||
pub fn set_corner_radius(radius: f32) {
|
||||
get!().set_corner_radius(radius)
|
||||
}
|
||||
|
||||
/// Returns the current corner radius for window borders.
|
||||
pub fn get_corner_radius() -> f32 {
|
||||
get!(0.0).get_corner_radius()
|
||||
}
|
||||
|
||||
/// Sets a callback to run when this config is unloaded.
|
||||
///
|
||||
/// Only one callback can be set at a time. If another callback is already set, it will be
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ use {
|
|||
tree::{
|
||||
DisplayNode, NodeIds, OutputNode, TearingMode, Transform, VrrMode,
|
||||
WorkspaceDisplayOrder, WorkspaceNode, container_layout, container_render_positions,
|
||||
container_render_titles, float_layout, float_titles, output_render_data,
|
||||
float_layout, output_render_data,
|
||||
placeholder_render_textures,
|
||||
},
|
||||
user_session::import_environment,
|
||||
|
|
@ -259,10 +259,8 @@ fn start_compositor2(
|
|||
theme: Default::default(),
|
||||
pending_container_layout: Default::default(),
|
||||
pending_container_render_positions: Default::default(),
|
||||
pending_container_render_title: Default::default(),
|
||||
pending_output_render_data: Default::default(),
|
||||
pending_float_layout: Default::default(),
|
||||
pending_float_titles: Default::default(),
|
||||
pending_input_popup_positioning: Default::default(),
|
||||
pending_toplevel_screencasts: Default::default(),
|
||||
pending_screencast_reallocs_or_reconfigures: Default::default(),
|
||||
|
|
@ -511,11 +509,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
|||
Phase::PostLayout,
|
||||
container_render_positions(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"container titles",
|
||||
Phase::PostLayout,
|
||||
container_render_titles(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"placeholder textures",
|
||||
Phase::PostLayout,
|
||||
|
|
@ -531,11 +524,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
|||
wlr_output_manager_done(state.clone()),
|
||||
),
|
||||
eng.spawn2("float layout", Phase::Layout, float_layout(state.clone())),
|
||||
eng.spawn2(
|
||||
"float titles",
|
||||
Phase::PostLayout,
|
||||
float_titles(state.clone()),
|
||||
),
|
||||
eng.spawn2("idle", Phase::PostLayout, idle(state.clone())),
|
||||
eng.spawn2(
|
||||
"input, popup positioning",
|
||||
|
|
|
|||
|
|
@ -1411,24 +1411,20 @@ impl ConfigProxyHandler {
|
|||
});
|
||||
}
|
||||
|
||||
fn handle_set_show_titles(&self, show: bool) {
|
||||
self.state.set_show_titles(show);
|
||||
fn handle_set_show_titles(&self, _show: bool) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
fn handle_get_show_titles(&self) {
|
||||
self.respond(Response::GetShowTitles {
|
||||
show: self.state.theme.show_titles.get(),
|
||||
});
|
||||
self.respond(Response::GetShowTitles { show: false });
|
||||
}
|
||||
|
||||
fn handle_set_floating_titles(&self, floating: bool) {
|
||||
self.state.set_floating_titles(floating);
|
||||
fn handle_set_floating_titles(&self, _floating: bool) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
fn handle_get_floating_titles(&self) {
|
||||
self.respond(Response::GetFloatingTitles {
|
||||
floating: self.state.theme.floating_titles.get(),
|
||||
});
|
||||
self.respond(Response::GetFloatingTitles { floating: false });
|
||||
}
|
||||
|
||||
fn handle_set_bar_position(&self, position: BarPosition) -> Result<(), CphError> {
|
||||
|
|
@ -1445,8 +1441,24 @@ impl ConfigProxyHandler {
|
|||
});
|
||||
}
|
||||
|
||||
fn handle_set_show_float_pin_icon(&self, show: bool) {
|
||||
self.state.set_show_pin_icon(show);
|
||||
fn handle_set_corner_radius(&self, radius: f32) {
|
||||
use crate::theme::CornerRadius;
|
||||
let radius = radius.max(0.0).min(1000.0);
|
||||
self.state
|
||||
.theme
|
||||
.corner_radius
|
||||
.set(CornerRadius::from(radius));
|
||||
self.state.damage(self.state.root.extents.get());
|
||||
}
|
||||
|
||||
fn handle_get_corner_radius(&self) {
|
||||
self.respond(Response::GetCornerRadius {
|
||||
radius: self.state.theme.corner_radius.get().top_left,
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_set_show_float_pin_icon(&self, _show: bool) {
|
||||
// no-op: titles have been removed, pin icon was in title bar
|
||||
}
|
||||
|
||||
fn handle_set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||
|
|
@ -2496,8 +2508,8 @@ impl ConfigProxyHandler {
|
|||
self.state.set_bar_font(Some(font));
|
||||
}
|
||||
|
||||
fn handle_set_title_font(&self, font: &str) {
|
||||
self.state.set_title_font(Some(font));
|
||||
fn handle_set_title_font(&self, _font: &str) {
|
||||
// no-op: titles have been removed
|
||||
}
|
||||
|
||||
fn handle_get_font(&self) {
|
||||
|
|
@ -3310,6 +3322,10 @@ impl ConfigProxyHandler {
|
|||
.handle_set_bar_position(position)
|
||||
.wrn("set_bar_position")?,
|
||||
ClientMessage::GetBarPosition => self.handle_get_bar_position(),
|
||||
ClientMessage::SetCornerRadius { radius } => {
|
||||
self.handle_set_corner_radius(radius)
|
||||
}
|
||||
ClientMessage::GetCornerRadius => self.handle_get_corner_radius(),
|
||||
ClientMessage::SeatFocusHistory { seat, timeline } => self
|
||||
.handle_seat_focus_history(seat, timeline)
|
||||
.wrn("seat_focus_history")?,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ pub enum GfxApiOpt {
|
|||
Sync,
|
||||
FillRect(FillRect),
|
||||
CopyTexture(CopyTexture),
|
||||
RoundedFillRect(RoundedFillRect),
|
||||
RoundedCopyTexture(RoundedCopyTexture),
|
||||
}
|
||||
|
||||
pub struct GfxRenderPass {
|
||||
|
|
@ -289,6 +291,53 @@ pub struct CopyTexture {
|
|||
pub alpha_mode: AlphaMode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RoundedFillRect {
|
||||
pub rect: FramebufferRect,
|
||||
pub color: Color,
|
||||
pub alpha: Option<f32>,
|
||||
pub render_intent: RenderIntent,
|
||||
pub cd: Rc<LinearColorDescription>,
|
||||
/// Size of the rectangle in physical pixels.
|
||||
pub size: [f32; 2],
|
||||
/// Per-corner radius in physical pixels: [top_left, top_right, bottom_right, bottom_left].
|
||||
pub corner_radius: [f32; 4],
|
||||
/// Border width in physical pixels. 0 means a filled rounded rect (no cutout).
|
||||
pub border_width: f32,
|
||||
/// Output scale for antialiasing.
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
impl RoundedFillRect {
|
||||
pub fn effective_color(&self) -> Color {
|
||||
let mut color = self.color;
|
||||
if let Some(alpha) = self.alpha {
|
||||
color = color * alpha;
|
||||
}
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RoundedCopyTexture {
|
||||
pub tex: Rc<dyn GfxTexture>,
|
||||
pub source: SampleRect,
|
||||
pub target: FramebufferRect,
|
||||
pub buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
pub acquire_sync: AcquireSync,
|
||||
pub release_sync: ReleaseSync,
|
||||
pub alpha: Option<f32>,
|
||||
pub opaque: bool,
|
||||
pub render_intent: RenderIntent,
|
||||
pub cd: Rc<ColorDescription>,
|
||||
pub alpha_mode: AlphaMode,
|
||||
/// Size of the rectangle in physical pixels.
|
||||
pub size: [f32; 2],
|
||||
/// Per-corner radius in physical pixels: [top_left, top_right, bottom_right, bottom_left].
|
||||
pub corner_radius: [f32; 4],
|
||||
/// Output scale for antialiasing.
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SyncFile(pub Rc<OwnedFd>);
|
||||
|
||||
|
|
@ -705,8 +754,8 @@ impl dyn GfxFramebuffer {
|
|||
let (width, height) = self.logical_size(transform);
|
||||
Rect::new_saturating(0, 0, width, height)
|
||||
},
|
||||
icons: None,
|
||||
stretch: None,
|
||||
corner_radius: None,
|
||||
};
|
||||
cursor.render_hardware_cursor(&mut renderer);
|
||||
self.render(
|
||||
|
|
@ -1039,8 +1088,8 @@ pub fn create_render_pass(
|
|||
let (width, height) = logical_size(physical_size, transform);
|
||||
Rect::new_saturating(0, 0, width, height)
|
||||
},
|
||||
icons: state.icons.get(state, scale),
|
||||
stretch: None,
|
||||
corner_radius: None,
|
||||
};
|
||||
node.node_render(&mut renderer, 0, 0, None);
|
||||
if let Some(rect) = cursor_rect {
|
||||
|
|
@ -1256,6 +1305,8 @@ impl GfxRenderPass {
|
|||
return None;
|
||||
}
|
||||
GfxApiOpt::CopyTexture(ct) => break 'ct2 ct,
|
||||
GfxApiOpt::RoundedFillRect(_) => return None,
|
||||
GfxApiOpt::RoundedCopyTexture(_) => return None,
|
||||
}
|
||||
}
|
||||
return None;
|
||||
|
|
@ -1299,6 +1350,8 @@ impl GfxRenderPass {
|
|||
// Texture could be visible.
|
||||
return None;
|
||||
}
|
||||
GfxApiOpt::RoundedFillRect(_) => return None,
|
||||
GfxApiOpt::RoundedCopyTexture(_) => return None,
|
||||
}
|
||||
}
|
||||
if let Some(clear) = self.clear
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use {
|
|||
cmm::cmm_eotf::Eotf,
|
||||
gfx_api::{
|
||||
AcquireSync, CopyTexture, FdSync, FramebufferRect, GfxApiOpt, GfxContext, GfxError,
|
||||
GfxTexture, ReleaseSync, SyncFile,
|
||||
GfxTexture, ReleaseSync, RoundedCopyTexture, RoundedFillRect, SyncFile,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
egl::image::EglImage,
|
||||
|
|
@ -217,6 +217,14 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
|
|||
copy_tex.push(c);
|
||||
i += 1;
|
||||
}
|
||||
GfxApiOpt::RoundedFillRect(rf) => {
|
||||
render_rounded_fill(&fb.ctx, rf);
|
||||
i += 1;
|
||||
}
|
||||
GfxApiOpt::RoundedCopyTexture(ct) => {
|
||||
render_rounded_texture(&fb.ctx, ct);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if fill_rect.is_not_empty() {
|
||||
|
|
@ -269,6 +277,12 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
|
|||
{
|
||||
resv.set_sync(user, &file);
|
||||
}
|
||||
if let GfxApiOpt::RoundedCopyTexture(ct) = op
|
||||
&& ct.release_sync == ReleaseSync::Explicit
|
||||
&& let Some(resv) = &ct.buffer_resv
|
||||
{
|
||||
resv.set_sync(user, &file);
|
||||
}
|
||||
}
|
||||
return Some(file);
|
||||
}
|
||||
|
|
@ -376,6 +390,114 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_rounded_fill(ctx: &GlRenderContext, rf: &RoundedFillRect) {
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
let prog = &ctx.rounded_fill_prog;
|
||||
let pos = rf.rect.to_points();
|
||||
// geo_pos maps vertex corners to pixel coordinates within the rectangle
|
||||
let [w, h] = rf.size;
|
||||
let geo: [[f32; 2]; 4] = [[w, 0.0], [0.0, 0.0], [w, h], [0.0, h]];
|
||||
let [r, g, b, a] = rf.color.to_array(Eotf::Gamma22);
|
||||
let a = a * rf.alpha.unwrap_or(1.0);
|
||||
unsafe {
|
||||
(gles.glEnable)(GL_BLEND);
|
||||
(gles.glUseProgram)(prog.prog.prog);
|
||||
(gles.glUniform4f)(prog.color, r * a, g * a, b * a, a);
|
||||
(gles.glUniform2f)(prog.size, rf.size[0], rf.size[1]);
|
||||
(gles.glUniform4f)(
|
||||
prog.corner_radius,
|
||||
rf.corner_radius[0],
|
||||
rf.corner_radius[1],
|
||||
rf.corner_radius[2],
|
||||
rf.corner_radius[3],
|
||||
);
|
||||
(gles.glUniform1f)(prog.border_width, rf.border_width);
|
||||
(gles.glUniform1f)(prog.scale, rf.scale);
|
||||
(gles.glVertexAttribPointer)(prog.pos as _, 2, GL_FLOAT, GL_FALSE, 0, pos.as_ptr() as _);
|
||||
(gles.glVertexAttribPointer)(prog.geo as _, 2, GL_FLOAT, GL_FALSE, 0, geo.as_ptr() as _);
|
||||
(gles.glEnableVertexAttribArray)(prog.pos as _);
|
||||
(gles.glEnableVertexAttribArray)(prog.geo as _);
|
||||
(gles.glDrawArrays)(GL_TRIANGLE_STRIP, 0, 4);
|
||||
(gles.glDisableVertexAttribArray)(prog.pos as _);
|
||||
(gles.glDisableVertexAttribArray)(prog.geo as _);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_rounded_texture(ctx: &GlRenderContext, ct: &RoundedCopyTexture) {
|
||||
let Some(texture) = ct.tex.as_gl() else {
|
||||
log::error!("A non-OpenGL texture was passed into OpenGL");
|
||||
return;
|
||||
};
|
||||
if !texture.gl.contents_valid.get() {
|
||||
log::error!("Ignoring texture with invalid contents");
|
||||
return;
|
||||
}
|
||||
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
unsafe {
|
||||
handle_explicit_sync(ctx, texture.gl.img.as_ref(), &ct.acquire_sync);
|
||||
(gles.glActiveTexture)(GL_TEXTURE0);
|
||||
let target = image_target(texture.gl.external_only);
|
||||
(gles.glBindTexture)(target, texture.gl.tex);
|
||||
(gles.glTexParameteri)(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
let progs = match texture.gl.external_only {
|
||||
true => match &ctx.rounded_tex_external {
|
||||
Some(p) => p,
|
||||
_ => {
|
||||
log::error!(
|
||||
"Trying to render an external-only texture but context does not support the required extension"
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
false => &ctx.rounded_tex_internal,
|
||||
};
|
||||
let copy_type = match ct.alpha.is_some() {
|
||||
true => TexCopyType::Multiply,
|
||||
false => TexCopyType::Identity,
|
||||
};
|
||||
let source_type = TexSourceType::HasAlpha;
|
||||
(gles.glEnable)(GL_BLEND);
|
||||
let prog = &progs[copy_type][source_type];
|
||||
(gles.glUseProgram)(prog.prog.prog);
|
||||
(gles.glUniform1i)(prog.tex, 0);
|
||||
(gles.glUniform2f)(prog.size, ct.size[0], ct.size[1]);
|
||||
(gles.glUniform4f)(
|
||||
prog.corner_radius,
|
||||
ct.corner_radius[0],
|
||||
ct.corner_radius[1],
|
||||
ct.corner_radius[2],
|
||||
ct.corner_radius[3],
|
||||
);
|
||||
(gles.glUniform1f)(prog.scale, ct.scale);
|
||||
if let Some(alpha) = ct.alpha {
|
||||
(gles.glUniform1f)(prog.alpha, alpha);
|
||||
}
|
||||
let texcoord = ct.source.to_points();
|
||||
let pos = ct.target.to_points();
|
||||
let [w, h] = ct.size;
|
||||
let geo: [[f32; 2]; 4] = [[w, 0.0], [0.0, 0.0], [w, h], [0.0, h]];
|
||||
(gles.glVertexAttribPointer)(
|
||||
prog.texcoord as _,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
0,
|
||||
texcoord.as_ptr() as _,
|
||||
);
|
||||
(gles.glVertexAttribPointer)(prog.pos as _, 2, GL_FLOAT, GL_FALSE, 0, pos.as_ptr() as _);
|
||||
(gles.glVertexAttribPointer)(prog.geo as _, 2, GL_FLOAT, GL_FALSE, 0, geo.as_ptr() as _);
|
||||
(gles.glEnableVertexAttribArray)(prog.texcoord as _);
|
||||
(gles.glEnableVertexAttribArray)(prog.pos as _);
|
||||
(gles.glEnableVertexAttribArray)(prog.geo as _);
|
||||
(gles.glDrawArrays)(GL_TRIANGLE_STRIP, 0, 4);
|
||||
(gles.glDisableVertexAttribArray)(prog.texcoord as _);
|
||||
(gles.glDisableVertexAttribArray)(prog.pos as _);
|
||||
(gles.glDisableVertexAttribArray)(prog.geo as _);
|
||||
(gles.glBindTexture)(target, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_explicit_sync(ctx: &GlRenderContext, img: Option<&Rc<EglImage>>, sync: &AcquireSync) {
|
||||
let Some(sync_file) = sync.get_sync_file() else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ dynload! {
|
|||
glGetAttribLocation: unsafe fn(prog: GLuint, name: *const GLchar) -> GLint,
|
||||
glUniform1i: unsafe fn(location: GLint, v0: GLint),
|
||||
glUniform1f: unsafe fn(location: GLint, v0: GLfloat),
|
||||
glUniform2f: unsafe fn(location: GLint, v0: GLfloat, v1: GLfloat),
|
||||
glUniform4f: unsafe fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat),
|
||||
glVertexAttribPointer: unsafe fn(
|
||||
index: GLuint,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,29 @@ impl TexProg {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RoundedFillProg {
|
||||
pub(crate) prog: GlProgram,
|
||||
pub(crate) pos: GLint,
|
||||
pub(crate) geo: GLint,
|
||||
pub(crate) color: GLint,
|
||||
pub(crate) size: GLint,
|
||||
pub(crate) corner_radius: GLint,
|
||||
pub(crate) border_width: GLint,
|
||||
pub(crate) scale: GLint,
|
||||
}
|
||||
|
||||
pub(crate) struct RoundedTexProg {
|
||||
pub(crate) prog: GlProgram,
|
||||
pub(crate) pos: GLint,
|
||||
pub(crate) texcoord: GLint,
|
||||
pub(crate) geo: GLint,
|
||||
pub(crate) tex: GLint,
|
||||
pub(crate) alpha: GLint,
|
||||
pub(crate) size: GLint,
|
||||
pub(crate) corner_radius: GLint,
|
||||
pub(crate) scale: GLint,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Linearize)]
|
||||
pub(in crate::gfx_apis::gl) enum TexCopyType {
|
||||
Identity,
|
||||
|
|
@ -86,6 +109,12 @@ pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
|||
pub(crate) fill_prog_pos: GLint,
|
||||
pub(crate) fill_prog_color: GLint,
|
||||
|
||||
pub(crate) rounded_fill_prog: RoundedFillProg,
|
||||
pub(crate) rounded_tex_internal:
|
||||
StaticMap<TexCopyType, StaticMap<TexSourceType, RoundedTexProg>>,
|
||||
pub(crate) rounded_tex_external:
|
||||
Option<StaticMap<TexCopyType, StaticMap<TexSourceType, RoundedTexProg>>>,
|
||||
|
||||
pub(in crate::gfx_apis::gl) gl_state: RefCell<GfxGlState>,
|
||||
|
||||
pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser,
|
||||
|
|
@ -163,6 +192,74 @@ impl GlRenderContext {
|
|||
include_str!("../shaders/fill.frag.glsl"),
|
||||
)?
|
||||
};
|
||||
let rounded_fill_prog = unsafe {
|
||||
let prog = GlProgram::from_shaders(
|
||||
ctx,
|
||||
include_str!("../shaders/rounded_fill.vert.glsl"),
|
||||
include_str!("../shaders/rounded_fill.frag.glsl"),
|
||||
)?;
|
||||
RoundedFillProg {
|
||||
pos: prog.get_attrib_location(c"pos"),
|
||||
geo: prog.get_attrib_location(c"geo"),
|
||||
color: prog.get_uniform_location(c"color"),
|
||||
size: prog.get_uniform_location(c"size"),
|
||||
corner_radius: prog.get_uniform_location(c"corner_radius"),
|
||||
border_width: prog.get_uniform_location(c"border_width"),
|
||||
scale: prog.get_uniform_location(c"scale"),
|
||||
prog,
|
||||
}
|
||||
};
|
||||
let rounded_tex_vert = include_str!("../shaders/rounded_tex.vert.glsl");
|
||||
let rounded_tex_frag = include_str!("../shaders/rounded_tex.frag.glsl");
|
||||
let create_rounded_tex_programs = |external: bool| {
|
||||
let create_program = |alpha_multiplier: bool, alpha: bool| {
|
||||
let mut src = String::new();
|
||||
if external {
|
||||
src.push_str("#define EXTERNAL\n");
|
||||
}
|
||||
if alpha_multiplier {
|
||||
src.push_str("#define ALPHA_MULTIPLIER\n");
|
||||
}
|
||||
if alpha {
|
||||
src.push_str("#define ALPHA\n");
|
||||
}
|
||||
src.push_str(rounded_tex_frag);
|
||||
unsafe {
|
||||
let prog = GlProgram::from_shaders(ctx, rounded_tex_vert, &src)?;
|
||||
let alpha_loc = match alpha_multiplier {
|
||||
true => prog.get_uniform_location(c"alpha"),
|
||||
false => 0,
|
||||
};
|
||||
Ok::<_, RenderError>(RoundedTexProg {
|
||||
pos: prog.get_attrib_location(c"pos"),
|
||||
texcoord: prog.get_attrib_location(c"texcoord"),
|
||||
geo: prog.get_attrib_location(c"geo"),
|
||||
tex: prog.get_uniform_location(c"tex"),
|
||||
alpha: alpha_loc,
|
||||
size: prog.get_uniform_location(c"size"),
|
||||
corner_radius: prog.get_uniform_location(c"corner_radius"),
|
||||
scale: prog.get_uniform_location(c"scale"),
|
||||
prog,
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok::<_, RenderError>(static_map! {
|
||||
TexCopyType::Identity => static_map! {
|
||||
TexSourceType::Opaque => create_program(false, false)?,
|
||||
TexSourceType::HasAlpha => create_program(false, true)?,
|
||||
},
|
||||
TexCopyType::Multiply => static_map! {
|
||||
TexSourceType::Opaque => create_program(true, false)?,
|
||||
TexSourceType::HasAlpha => create_program(true, true)?,
|
||||
},
|
||||
})
|
||||
};
|
||||
let rounded_tex_internal = create_rounded_tex_programs(false)?;
|
||||
let rounded_tex_external = if ctx.ext.contains(GL_OES_EGL_IMAGE_EXTERNAL) {
|
||||
Some(create_rounded_tex_programs(true)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self {
|
||||
ctx: ctx.clone(),
|
||||
gbm: ctx.dpy.gbm.clone(),
|
||||
|
|
@ -177,6 +274,10 @@ impl GlRenderContext {
|
|||
fill_prog_color: unsafe { fill_prog.get_uniform_location(c"color") },
|
||||
fill_prog,
|
||||
|
||||
rounded_fill_prog,
|
||||
rounded_tex_internal,
|
||||
rounded_tex_external,
|
||||
|
||||
gl_state: Default::default(),
|
||||
|
||||
buffer_resv_user: Default::default(),
|
||||
|
|
|
|||
56
src/gfx_apis/gl/shaders/rounded_fill.frag.glsl
Normal file
56
src/gfx_apis/gl/shaders/rounded_fill.frag.glsl
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
precision mediump float;
|
||||
|
||||
varying vec2 v_geo;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform vec2 size;
|
||||
uniform vec4 corner_radius;
|
||||
uniform float border_width;
|
||||
uniform float scale;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 half_size, vec4 radii) {
|
||||
float radius;
|
||||
vec2 center;
|
||||
|
||||
if (coords.x < half_size.x && coords.y < half_size.y) {
|
||||
radius = radii.x; // top-left
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x >= half_size.x && coords.y < half_size.y) {
|
||||
radius = radii.y; // top-right
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x >= half_size.x && coords.y >= half_size.y) {
|
||||
radius = radii.z; // bottom-right
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else {
|
||||
radius = radii.w; // bottom-left
|
||||
center = vec2(radius, size.y - radius);
|
||||
}
|
||||
|
||||
if (radius == 0.0)
|
||||
return 1.0;
|
||||
|
||||
float dist = distance(coords, center);
|
||||
float half_px = 0.5 / scale;
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 half_size = size / 2.0;
|
||||
float outer_alpha = rounding_alpha(v_geo, half_size, corner_radius);
|
||||
|
||||
float inner_alpha = 0.0;
|
||||
if (border_width > 0.0) {
|
||||
vec2 inner_coords = v_geo - vec2(border_width);
|
||||
vec2 inner_size = size - vec2(border_width * 2.0);
|
||||
vec2 inner_half = inner_size / 2.0;
|
||||
vec4 inner_radii = max(corner_radius - vec4(border_width), vec4(0.0));
|
||||
|
||||
if (inner_coords.x >= 0.0 && inner_coords.y >= 0.0 &&
|
||||
inner_coords.x <= inner_size.x && inner_coords.y <= inner_size.y) {
|
||||
inner_alpha = rounding_alpha(inner_coords, inner_half, inner_radii);
|
||||
}
|
||||
}
|
||||
|
||||
float a = outer_alpha * (1.0 - inner_alpha);
|
||||
gl_FragColor = color * a;
|
||||
}
|
||||
8
src/gfx_apis/gl/shaders/rounded_fill.vert.glsl
Normal file
8
src/gfx_apis/gl/shaders/rounded_fill.vert.glsl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
attribute vec2 pos;
|
||||
attribute vec2 geo;
|
||||
varying vec2 v_geo;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
v_geo = geo;
|
||||
}
|
||||
71
src/gfx_apis/gl/shaders/rounded_tex.frag.glsl
Normal file
71
src/gfx_apis/gl/shaders/rounded_tex.frag.glsl
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#ifdef EXTERNAL
|
||||
#extension GL_OES_EGL_image_external : require
|
||||
#endif
|
||||
|
||||
precision mediump float;
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
varying vec2 v_geo;
|
||||
|
||||
#ifdef EXTERNAL
|
||||
uniform samplerExternalOES tex;
|
||||
#else
|
||||
uniform sampler2D tex;
|
||||
#endif
|
||||
#ifdef ALPHA_MULTIPLIER
|
||||
uniform float alpha;
|
||||
#endif
|
||||
uniform vec2 size;
|
||||
uniform vec4 corner_radius;
|
||||
uniform float scale;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 half_size, vec4 radii) {
|
||||
float radius;
|
||||
vec2 center;
|
||||
|
||||
if (coords.x < half_size.x && coords.y < half_size.y) {
|
||||
radius = radii.x; // top-left
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x >= half_size.x && coords.y < half_size.y) {
|
||||
radius = radii.y; // top-right
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x >= half_size.x && coords.y >= half_size.y) {
|
||||
radius = radii.z; // bottom-right
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else {
|
||||
radius = radii.w; // bottom-left
|
||||
center = vec2(radius, size.y - radius);
|
||||
}
|
||||
|
||||
if (radius == 0.0)
|
||||
return 1.0;
|
||||
|
||||
float dist = distance(coords, center);
|
||||
float half_px = 0.5 / scale;
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 half_size = size / 2.0;
|
||||
float ra = rounding_alpha(v_geo, half_size, corner_radius);
|
||||
|
||||
#ifdef ALPHA
|
||||
|
||||
#ifdef ALPHA_MULTIPLIER
|
||||
gl_FragColor = texture2D(tex, v_texcoord) * alpha * ra;
|
||||
#else // !ALPHA_MULTIPLIER
|
||||
gl_FragColor = texture2D(tex, v_texcoord) * ra;
|
||||
#endif // ALPHA_MULTIPLIER
|
||||
|
||||
#else // !ALPHA
|
||||
|
||||
#ifdef ALPHA_MULTIPLIER
|
||||
vec4 tc = texture2D(tex, v_texcoord);
|
||||
gl_FragColor = vec4(tc.rgb * alpha * ra, alpha * ra);
|
||||
#else // !ALPHA_MULTIPLIER
|
||||
vec4 tc = texture2D(tex, v_texcoord);
|
||||
gl_FragColor = vec4(tc.rgb * ra, ra);
|
||||
#endif // ALPHA_MULTIPLIER
|
||||
|
||||
#endif // ALPHA
|
||||
}
|
||||
11
src/gfx_apis/gl/shaders/rounded_tex.vert.glsl
Normal file
11
src/gfx_apis/gl/shaders/rounded_tex.vert.glsl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
attribute vec2 pos;
|
||||
attribute vec2 texcoord;
|
||||
attribute vec2 geo;
|
||||
varying vec2 v_texcoord;
|
||||
varying vec2 v_geo;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
v_texcoord = texcoord;
|
||||
v_geo = geo;
|
||||
}
|
||||
|
|
@ -27,9 +27,14 @@ use {
|
|||
semaphore::VulkanSemaphore,
|
||||
shaders::{
|
||||
ColorManagementData, EotfArgs, FILL_FRAG, FILL_VERT, FillPushConstants,
|
||||
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_TEX_FRAG, LEGACY_TEX_VERT,
|
||||
LegacyFillPushConstants, LegacyTexPushConstants, OUT_FRAG, OUT_VERT,
|
||||
OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex, VulkanShader,
|
||||
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_ROUNDED_FILL_FRAG,
|
||||
LEGACY_ROUNDED_FILL_VERT, LEGACY_ROUNDED_TEX_FRAG, LEGACY_ROUNDED_TEX_VERT,
|
||||
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants,
|
||||
LegacyRoundedFillPushConstants, LegacyRoundedTexPushConstants,
|
||||
LegacyTexPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants, ROUNDED_FILL_FRAG,
|
||||
ROUNDED_FILL_VERT, ROUNDED_TEX_FRAG, ROUNDED_TEX_VERT, RoundedFillPushConstants,
|
||||
RoundedTexPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex,
|
||||
VulkanShader,
|
||||
},
|
||||
},
|
||||
io_uring::IoUring,
|
||||
|
|
@ -104,6 +109,13 @@ pub struct VulkanRenderer {
|
|||
pub(super) tex_frag_shader: Rc<VulkanShader>,
|
||||
pub(super) out_vert_shader: Option<Rc<VulkanShader>>,
|
||||
pub(super) out_frag_shader: Option<Rc<VulkanShader>>,
|
||||
pub(super) rounded_fill_vert_shader: Rc<VulkanShader>,
|
||||
pub(super) rounded_fill_frag_shader: Rc<VulkanShader>,
|
||||
pub(super) rounded_tex_vert_shader: Rc<VulkanShader>,
|
||||
pub(super) rounded_tex_frag_shader: Rc<VulkanShader>,
|
||||
pub(super) rounded_fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
|
||||
pub(super) rounded_tex_pipelines:
|
||||
StaticMap<VulkanEotf, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
|
||||
pub(super) tex_descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
|
||||
pub(super) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||
pub(super) defunct: Cell<bool>,
|
||||
|
|
@ -202,6 +214,8 @@ type Point = [[f32; 2]; 4];
|
|||
enum VulkanOp {
|
||||
Fill(VulkanFillOp),
|
||||
Tex(VulkanTexOp),
|
||||
RoundedFill(VulkanRoundedFillOp),
|
||||
RoundedTex(VulkanRoundedTexOp),
|
||||
}
|
||||
|
||||
struct VulkanTexOp {
|
||||
|
|
@ -231,6 +245,39 @@ struct VulkanFillOp {
|
|||
instances: u32,
|
||||
}
|
||||
|
||||
struct VulkanRoundedFillOp {
|
||||
target: Point,
|
||||
color: [f32; 4],
|
||||
source_type: TexSourceType,
|
||||
size: [f32; 2],
|
||||
corner_radius: [f32; 4],
|
||||
border_width: f32,
|
||||
scale: f32,
|
||||
range_address: DeviceAddress,
|
||||
}
|
||||
|
||||
struct VulkanRoundedTexOp {
|
||||
tex: Rc<VulkanImage>,
|
||||
index: usize,
|
||||
target: Point,
|
||||
source: Point,
|
||||
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
acquire_sync: Option<AcquireSync>,
|
||||
release_sync: ReleaseSync,
|
||||
alpha: f32,
|
||||
source_type: TexSourceType,
|
||||
copy_type: TexCopyType,
|
||||
alpha_mode: AlphaMode,
|
||||
tex_cd: Rc<ColorDescription>,
|
||||
color_management_data_address: Option<DeviceAddress>,
|
||||
eotf_args_address: Option<DeviceAddress>,
|
||||
resource_descriptor_buffer_offset: DeviceAddress,
|
||||
size: [f32; 2],
|
||||
corner_radius: [f32; 4],
|
||||
scale: f32,
|
||||
range_address: DeviceAddress,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)]
|
||||
pub(super) enum RenderPass {
|
||||
BlendBuffer,
|
||||
|
|
@ -295,12 +342,20 @@ impl VulkanDevice {
|
|||
let tex_frag_shader;
|
||||
let out_vert_shader;
|
||||
let out_frag_shader;
|
||||
let rounded_fill_vert_shader;
|
||||
let rounded_fill_frag_shader;
|
||||
let rounded_tex_vert_shader;
|
||||
let rounded_tex_frag_shader;
|
||||
let mut tex_descriptor_set_layouts = ArrayVec::new();
|
||||
if self.descriptor_buffer.is_some() {
|
||||
tex_vert_shader = self.create_shader(TEX_VERT)?;
|
||||
tex_frag_shader = self.create_shader(TEX_FRAG)?;
|
||||
fill_vert_shader = self.create_shader(FILL_VERT)?;
|
||||
fill_frag_shader = self.create_shader(FILL_FRAG)?;
|
||||
rounded_fill_vert_shader = self.create_shader(ROUNDED_FILL_VERT)?;
|
||||
rounded_fill_frag_shader = self.create_shader(ROUNDED_FILL_FRAG)?;
|
||||
rounded_tex_vert_shader = self.create_shader(ROUNDED_TEX_VERT)?;
|
||||
rounded_tex_frag_shader = self.create_shader(ROUNDED_TEX_FRAG)?;
|
||||
out_vert_shader = Some(self.create_shader(OUT_VERT)?);
|
||||
out_frag_shader = Some(self.create_shader(OUT_FRAG)?);
|
||||
tex_descriptor_set_layouts
|
||||
|
|
@ -311,6 +366,10 @@ impl VulkanDevice {
|
|||
tex_frag_shader = self.create_shader(LEGACY_TEX_FRAG)?;
|
||||
fill_vert_shader = self.create_shader(LEGACY_FILL_VERT)?;
|
||||
fill_frag_shader = self.create_shader(LEGACY_FILL_FRAG)?;
|
||||
rounded_fill_vert_shader = self.create_shader(LEGACY_ROUNDED_FILL_VERT)?;
|
||||
rounded_fill_frag_shader = self.create_shader(LEGACY_ROUNDED_FILL_FRAG)?;
|
||||
rounded_tex_vert_shader = self.create_shader(LEGACY_ROUNDED_TEX_VERT)?;
|
||||
rounded_tex_frag_shader = self.create_shader(LEGACY_ROUNDED_TEX_FRAG)?;
|
||||
out_vert_shader = None;
|
||||
out_frag_shader = None;
|
||||
tex_descriptor_set_layouts
|
||||
|
|
@ -400,6 +459,12 @@ impl VulkanDevice {
|
|||
tex_frag_shader,
|
||||
out_vert_shader,
|
||||
out_frag_shader,
|
||||
rounded_fill_vert_shader,
|
||||
rounded_fill_frag_shader,
|
||||
rounded_tex_vert_shader,
|
||||
rounded_tex_frag_shader,
|
||||
rounded_fill_pipelines: Default::default(),
|
||||
rounded_tex_pipelines: Default::default(),
|
||||
tex_descriptor_set_layouts,
|
||||
out_descriptor_set_layout,
|
||||
defunct: Cell::new(false),
|
||||
|
|
@ -457,6 +522,112 @@ impl VulkanRenderer {
|
|||
Ok(fill_pipelines)
|
||||
}
|
||||
|
||||
fn get_or_create_rounded_fill_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
) -> Result<FillPipelines, VulkanError> {
|
||||
if let Some(pl) = self.rounded_fill_pipelines.get(&format) {
|
||||
return Ok(pl);
|
||||
}
|
||||
let create_pipeline = |src_has_alpha| {
|
||||
let push_size = if self.device.descriptor_buffer.is_some() {
|
||||
size_of::<RoundedFillPushConstants>()
|
||||
} else {
|
||||
size_of::<LegacyRoundedFillPushConstants>()
|
||||
};
|
||||
let info = PipelineCreateInfo {
|
||||
format,
|
||||
vert: self.rounded_fill_vert_shader.clone(),
|
||||
frag: self.rounded_fill_frag_shader.clone(),
|
||||
blend: src_has_alpha,
|
||||
src_has_alpha,
|
||||
has_alpha_mult: false,
|
||||
alpha_mode: AlphaMode::PremultipliedOptical,
|
||||
eotf: EOTF_LINEAR,
|
||||
inv_eotf: EOTF_LINEAR,
|
||||
descriptor_set_layouts: Default::default(),
|
||||
has_color_management_data: false,
|
||||
};
|
||||
self.device.create_pipeline2(info, push_size)
|
||||
};
|
||||
let pipelines = Rc::new(static_map! {
|
||||
TexSourceType::HasAlpha => create_pipeline(true)?,
|
||||
TexSourceType::Opaque => create_pipeline(false)?,
|
||||
});
|
||||
self.rounded_fill_pipelines.set(format, pipelines.clone());
|
||||
Ok(pipelines)
|
||||
}
|
||||
|
||||
fn get_or_create_rounded_tex_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
target_cd: &ColorDescription,
|
||||
) -> Rc<TexPipelines> {
|
||||
let eotf = target_cd.eotf.to_vulkan();
|
||||
let pipelines = &self.rounded_tex_pipelines[eotf];
|
||||
match pipelines.get(&format) {
|
||||
Some(pl) => pl,
|
||||
_ => {
|
||||
let pl = Rc::new(TexPipelines {
|
||||
format,
|
||||
eotf,
|
||||
pipelines: Default::default(),
|
||||
});
|
||||
pipelines.set(format, pl.clone());
|
||||
pl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_rounded_tex_pipeline(
|
||||
&self,
|
||||
pipelines: &TexPipelines,
|
||||
tex_cd: &ColorDescription,
|
||||
tex_copy_type: TexCopyType,
|
||||
tex_source_type: TexSourceType,
|
||||
mut tex_alpha_mode: AlphaMode,
|
||||
has_color_management_data: bool,
|
||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||
if tex_source_type == TexSourceType::Opaque {
|
||||
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
|
||||
}
|
||||
let key = TexPipelineKey {
|
||||
tex_copy_type,
|
||||
tex_source_type,
|
||||
tex_alpha_mode,
|
||||
eotf: tex_cd.eotf.to_vulkan(),
|
||||
has_color_management_data,
|
||||
};
|
||||
if let Some(pl) = pipelines.pipelines.get(&key) {
|
||||
return Ok(pl);
|
||||
}
|
||||
let has_alpha_mult = match tex_copy_type {
|
||||
TexCopyType::Identity => false,
|
||||
TexCopyType::Multiply => true,
|
||||
};
|
||||
let push_size = if self.device.descriptor_buffer.is_some() {
|
||||
size_of::<RoundedTexPushConstants>()
|
||||
} else {
|
||||
size_of::<LegacyRoundedTexPushConstants>()
|
||||
};
|
||||
let info = PipelineCreateInfo {
|
||||
format: pipelines.format,
|
||||
vert: self.rounded_tex_vert_shader.clone(),
|
||||
frag: self.rounded_tex_frag_shader.clone(),
|
||||
blend: true, // always blend since corners are transparent
|
||||
src_has_alpha: true, // rounding makes everything have alpha
|
||||
has_alpha_mult,
|
||||
alpha_mode: key.tex_alpha_mode,
|
||||
eotf: key.eotf.to_vulkan(),
|
||||
inv_eotf: pipelines.eotf.to_vulkan(),
|
||||
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
|
||||
has_color_management_data,
|
||||
};
|
||||
let pl = self.device.create_pipeline2(info, push_size)?;
|
||||
pipelines.pipelines.set(key, pl.clone());
|
||||
Ok(pl)
|
||||
}
|
||||
|
||||
fn get_or_create_tex_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
|
|
@ -665,23 +836,34 @@ impl VulkanRenderer {
|
|||
RenderPass::FrameBuffer => fb_inv_eotf_args_descriptor,
|
||||
};
|
||||
for cmd in &mut memory.ops[pass] {
|
||||
let VulkanOp::Tex(c) = cmd else {
|
||||
continue;
|
||||
let (tex, resource_offset, cm_addr, eotf_addr) = match cmd {
|
||||
VulkanOp::Tex(c) => (
|
||||
&c.tex,
|
||||
&mut c.resource_descriptor_buffer_offset,
|
||||
&c.color_management_data_address,
|
||||
&c.eotf_args_address,
|
||||
),
|
||||
VulkanOp::RoundedTex(c) => (
|
||||
&c.tex,
|
||||
&mut c.resource_descriptor_buffer_offset,
|
||||
&c.color_management_data_address,
|
||||
&c.eotf_args_address,
|
||||
),
|
||||
_ => continue,
|
||||
};
|
||||
let tex = &c.tex;
|
||||
c.resource_descriptor_buffer_offset = resource_writer.next_offset();
|
||||
*resource_offset = resource_writer.next_offset();
|
||||
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
||||
writer.write(
|
||||
tex_descriptor_set_layout.offsets[0],
|
||||
tex.sampled_image_descriptor.as_ref().unwrap(),
|
||||
);
|
||||
if let Some(addr) = c.color_management_data_address {
|
||||
if let Some(addr) = *cm_addr {
|
||||
writer.write(
|
||||
tex_descriptor_set_layout.offsets[1],
|
||||
get_ub_descriptor!(addr, ColorManagementData),
|
||||
);
|
||||
}
|
||||
if let Some(addr) = c.eotf_args_address {
|
||||
if let Some(addr) = *eotf_addr {
|
||||
writer.write(
|
||||
tex_descriptor_set_layout.offsets[2],
|
||||
get_ub_descriptor!(addr, EotfArgs),
|
||||
|
|
@ -741,12 +923,18 @@ impl VulkanRenderer {
|
|||
enum Key {
|
||||
Fill { color: [u32; 4] },
|
||||
Tex(usize),
|
||||
RoundedFill { color: [u32; 4] },
|
||||
RoundedTex(usize),
|
||||
}
|
||||
match o {
|
||||
VulkanOp::Fill(f) => Key::Fill {
|
||||
color: f.color.map(|c| c.to_bits()),
|
||||
},
|
||||
VulkanOp::Tex(t) => Key::Tex(t.index),
|
||||
VulkanOp::RoundedFill(f) => Key::RoundedFill {
|
||||
color: f.color.map(|c| c.to_bits()),
|
||||
},
|
||||
VulkanOp::RoundedTex(t) => Key::RoundedTex(t.index),
|
||||
}
|
||||
});
|
||||
let mops = &mut memory.ops[pass];
|
||||
|
|
@ -782,6 +970,22 @@ impl VulkanRenderer {
|
|||
}
|
||||
mops.push(VulkanOp::Tex(c));
|
||||
}
|
||||
VulkanOp::RoundedFill(mut f) => {
|
||||
f.range_address = memory.data_buffer.len() as DeviceAddress;
|
||||
memory.data_buffer.extend_from_slice(uapi::as_bytes(&f.target));
|
||||
mops.push(VulkanOp::RoundedFill(f));
|
||||
}
|
||||
VulkanOp::RoundedTex(mut c) => {
|
||||
c.range_address = memory.data_buffer.len() as DeviceAddress;
|
||||
let vertex = TexVertex {
|
||||
pos: c.target,
|
||||
tex_pos: c.source,
|
||||
};
|
||||
memory
|
||||
.data_buffer
|
||||
.extend_from_slice(uapi::as_bytes(&vertex));
|
||||
mops.push(VulkanOp::RoundedTex(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -917,6 +1121,105 @@ impl VulkanRenderer {
|
|||
}));
|
||||
}
|
||||
}
|
||||
GfxApiOpt::RoundedFillRect(rf) => {
|
||||
let target = rf.rect.to_points();
|
||||
for pass in RenderPass::variants() {
|
||||
let Some(bounds) = memory.paint_bounds[pass] else {
|
||||
continue;
|
||||
};
|
||||
if !bounds.intersects(&target) {
|
||||
continue;
|
||||
}
|
||||
let target_cd = match pass {
|
||||
RenderPass::BlendBuffer => blend_cd,
|
||||
RenderPass::FrameBuffer => fb_cd,
|
||||
};
|
||||
let tf = target_cd.eotf;
|
||||
let color = memory.color_transforms.apply_to_color(
|
||||
&rf.cd,
|
||||
target_cd,
|
||||
rf.render_intent,
|
||||
rf.color,
|
||||
);
|
||||
let color = color.to_array2(tf, rf.alpha);
|
||||
let source_type = TexSourceType::HasAlpha;
|
||||
memory.ops_tmp[pass].push(VulkanOp::RoundedFill(VulkanRoundedFillOp {
|
||||
target,
|
||||
color,
|
||||
source_type,
|
||||
size: rf.size,
|
||||
corner_radius: rf.corner_radius,
|
||||
border_width: rf.border_width,
|
||||
scale: rf.scale,
|
||||
range_address: 0,
|
||||
}));
|
||||
}
|
||||
}
|
||||
GfxApiOpt::RoundedCopyTexture(ct) => {
|
||||
let tex = ct.tex.clone().into_vk(&self.device.device)?;
|
||||
if tex.contents_are_undefined.get() {
|
||||
log::warn!("Ignoring undefined texture");
|
||||
continue;
|
||||
}
|
||||
if tex.queue_state.get().acquire(QueueFamily::Gfx) == QueueTransfer::Impossible
|
||||
{
|
||||
log::warn!("Ignoring texture owned by different queue");
|
||||
continue;
|
||||
}
|
||||
let target = ct.target.to_points();
|
||||
let source = ct.source.to_points();
|
||||
for pass in RenderPass::variants() {
|
||||
let Some(bounds) = memory.paint_bounds[pass] else {
|
||||
continue;
|
||||
};
|
||||
if !bounds.intersects(&target) {
|
||||
continue;
|
||||
}
|
||||
let copy_type = match ct.alpha.is_some() {
|
||||
true => TexCopyType::Multiply,
|
||||
false => TexCopyType::Identity,
|
||||
};
|
||||
let source_type = TexSourceType::HasAlpha;
|
||||
let target_cd = match pass {
|
||||
RenderPass::BlendBuffer => blend_cd,
|
||||
RenderPass::FrameBuffer => fb_cd,
|
||||
};
|
||||
let color_management_data_address = memory.color_transforms.get_offset(
|
||||
&ct.cd.linear,
|
||||
target_cd,
|
||||
ct.render_intent,
|
||||
self.device.uniform_buffer_offset_mask,
|
||||
&mut memory.uniform_buffer_writer,
|
||||
);
|
||||
let eotf_args_address = memory.eotf_args_cache.get_offset(
|
||||
&ct.cd,
|
||||
false,
|
||||
self.device.uniform_buffer_offset_mask,
|
||||
&mut memory.uniform_buffer_writer,
|
||||
);
|
||||
memory.ops_tmp[pass].push(VulkanOp::RoundedTex(VulkanRoundedTexOp {
|
||||
tex: tex.clone(),
|
||||
index,
|
||||
target,
|
||||
source,
|
||||
buffer_resv: ct.buffer_resv.clone(),
|
||||
acquire_sync: Some(ct.acquire_sync.clone()),
|
||||
release_sync: ct.release_sync,
|
||||
alpha: ct.alpha.unwrap_or_default(),
|
||||
source_type,
|
||||
copy_type,
|
||||
alpha_mode: ct.alpha_mode,
|
||||
tex_cd: ct.cd.clone(),
|
||||
color_management_data_address,
|
||||
eotf_args_address,
|
||||
resource_descriptor_buffer_offset: 0,
|
||||
size: ct.size,
|
||||
corner_radius: ct.corner_radius,
|
||||
scale: ct.scale,
|
||||
range_address: 0,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sync(memory);
|
||||
|
|
@ -998,6 +1301,12 @@ impl VulkanRenderer {
|
|||
VulkanOp::Tex(c) => {
|
||||
c.range_address += buffer.buffer.address;
|
||||
}
|
||||
VulkanOp::RoundedFill(f) => {
|
||||
f.range_address += buffer.buffer.address;
|
||||
}
|
||||
VulkanOp::RoundedTex(c) => {
|
||||
c.range_address += buffer.buffer.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1029,10 +1338,17 @@ impl VulkanRenderer {
|
|||
}
|
||||
for ops in memory.ops.values_mut() {
|
||||
for op in ops {
|
||||
if let VulkanOp::Tex(c) = op {
|
||||
match op {
|
||||
VulkanOp::Tex(c) => {
|
||||
adj!(&mut c.color_management_data_address);
|
||||
adj!(&mut c.eotf_args_address);
|
||||
}
|
||||
VulkanOp::RoundedTex(c) => {
|
||||
adj!(&mut c.color_management_data_address);
|
||||
adj!(&mut c.eotf_args_address);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
adj!(&mut memory.blend_buffer_color_management_data_address);
|
||||
|
|
@ -1051,8 +1367,12 @@ impl VulkanRenderer {
|
|||
let execution = self.allocate_point();
|
||||
for pass in RenderPass::variants() {
|
||||
for cmd in &mut memory.ops[pass] {
|
||||
if let VulkanOp::Tex(c) = cmd {
|
||||
let tex = &c.tex;
|
||||
let tex_data = match cmd {
|
||||
VulkanOp::Tex(c) => Some((&c.tex, &mut c.buffer_resv, &mut c.acquire_sync, c.release_sync)),
|
||||
VulkanOp::RoundedTex(c) => Some((&c.tex, &mut c.buffer_resv, &mut c.acquire_sync, c.release_sync)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some((tex, buffer_resv, acquire_sync, release_sync)) = tex_data {
|
||||
if tex.execution_version.replace(execution) == execution {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1066,9 +1386,9 @@ impl VulkanRenderer {
|
|||
}
|
||||
memory.textures.push(UsedTexture {
|
||||
tex: tex.clone(),
|
||||
resv: c.buffer_resv.take(),
|
||||
acquire_sync: c.acquire_sync.take().unwrap(),
|
||||
release_sync: c.release_sync,
|
||||
resv: buffer_resv.take(),
|
||||
acquire_sync: acquire_sync.take().unwrap(),
|
||||
release_sync,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1301,6 +1621,10 @@ impl VulkanRenderer {
|
|||
let memory = &*self.memory.borrow();
|
||||
let fill_pl = self.get_or_create_fill_pipelines(target.format.vk_format)?;
|
||||
let tex_pl = self.get_or_create_tex_pipelines(target.format.vk_format, target_cd);
|
||||
let rounded_fill_pl =
|
||||
self.get_or_create_rounded_fill_pipelines(target.format.vk_format)?;
|
||||
let rounded_tex_pl =
|
||||
self.get_or_create_rounded_tex_pipelines(target.format.vk_format, target_cd);
|
||||
let dev = &self.device.device;
|
||||
let mut current_pipeline = None;
|
||||
let mut bind = |pipeline: &VulkanPipeline| {
|
||||
|
|
@ -1421,6 +1745,134 @@ impl VulkanRenderer {
|
|||
}
|
||||
}
|
||||
}
|
||||
VulkanOp::RoundedFill(r) => {
|
||||
let pipeline = &rounded_fill_pl[r.source_type];
|
||||
bind(pipeline);
|
||||
if self.device.descriptor_buffer.is_some() {
|
||||
let push = RoundedFillPushConstants {
|
||||
color: r.color,
|
||||
vertices: r.range_address,
|
||||
size: r.size,
|
||||
corner_radius: r.corner_radius,
|
||||
border_width: r.border_width,
|
||||
scale: r.scale,
|
||||
};
|
||||
unsafe {
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
pipeline.pipeline_layout,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
0,
|
||||
uapi::as_bytes(&push),
|
||||
);
|
||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||
}
|
||||
} else {
|
||||
let push = LegacyRoundedFillPushConstants {
|
||||
pos: r.target,
|
||||
color: r.color,
|
||||
size_x: r.size[0],
|
||||
size_y: r.size[1],
|
||||
corner_radius_tl: r.corner_radius[0],
|
||||
corner_radius_tr: r.corner_radius[1],
|
||||
corner_radius_br: r.corner_radius[2],
|
||||
corner_radius_bl: r.corner_radius[3],
|
||||
border_width: r.border_width,
|
||||
scale: r.scale,
|
||||
};
|
||||
unsafe {
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
pipeline.pipeline_layout,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
0,
|
||||
uapi::as_bytes(&push),
|
||||
);
|
||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
VulkanOp::RoundedTex(c) => {
|
||||
let tex = &c.tex;
|
||||
let pipeline = self.get_or_create_rounded_tex_pipeline(
|
||||
&rounded_tex_pl,
|
||||
&c.tex_cd,
|
||||
c.copy_type,
|
||||
c.source_type,
|
||||
c.alpha_mode,
|
||||
c.color_management_data_address.is_some(),
|
||||
)?;
|
||||
bind(&pipeline);
|
||||
let image_info = DescriptorImageInfo::default()
|
||||
.image_view(tex.texture_view)
|
||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||
if let Some(db) = &self.device.descriptor_buffer {
|
||||
let push = RoundedTexPushConstants {
|
||||
vertices: c.range_address,
|
||||
alpha: c.alpha,
|
||||
size_x: c.size[0],
|
||||
size_y: c.size[1],
|
||||
corner_radius_tl: c.corner_radius[0],
|
||||
corner_radius_tr: c.corner_radius[1],
|
||||
corner_radius_br: c.corner_radius[2],
|
||||
corner_radius_bl: c.corner_radius[3],
|
||||
scale: c.scale,
|
||||
};
|
||||
unsafe {
|
||||
db.cmd_set_descriptor_buffer_offsets(
|
||||
buf,
|
||||
PipelineBindPoint::GRAPHICS,
|
||||
pipeline.pipeline_layout,
|
||||
0,
|
||||
&[0, 1],
|
||||
&[0, c.resource_descriptor_buffer_offset],
|
||||
);
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
pipeline.pipeline_layout,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
0,
|
||||
uapi::as_bytes(&push),
|
||||
);
|
||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||
}
|
||||
} else {
|
||||
let write_descriptor_set = WriteDescriptorSet::default()
|
||||
.descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||
.image_info(slice::from_ref(&image_info));
|
||||
unsafe {
|
||||
self.device.push_descriptor.cmd_push_descriptor_set(
|
||||
buf,
|
||||
PipelineBindPoint::GRAPHICS,
|
||||
pipeline.pipeline_layout,
|
||||
0,
|
||||
slice::from_ref(&write_descriptor_set),
|
||||
);
|
||||
}
|
||||
let push = LegacyRoundedTexPushConstants {
|
||||
pos: c.target,
|
||||
tex_pos: c.source,
|
||||
alpha: c.alpha,
|
||||
size_x: c.size[0],
|
||||
size_y: c.size[1],
|
||||
corner_radius_tl: c.corner_radius[0],
|
||||
corner_radius_tr: c.corner_radius[1],
|
||||
corner_radius_br: c.corner_radius[2],
|
||||
corner_radius_bl: c.corner_radius[3],
|
||||
scale: c.scale,
|
||||
};
|
||||
unsafe {
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
pipeline.pipeline_layout,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
0,
|
||||
uapi::as_bytes(&push),
|
||||
);
|
||||
dev.cmd_draw(buf, 4, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -1933,6 +2385,13 @@ impl VulkanRenderer {
|
|||
};
|
||||
(opaque, c.target)
|
||||
}
|
||||
GfxApiOpt::RoundedFillRect(_) => {
|
||||
// Rounded rects are never fully opaque due to AA at corners
|
||||
continue;
|
||||
}
|
||||
GfxApiOpt::RoundedCopyTexture(_) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if opaque || bb.is_none() {
|
||||
tag |= 1;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,18 @@ pub const LEGACY_FILL_VERT: &[u8] = include_bytes!("shaders_bin/legacy_fill.vert
|
|||
pub const LEGACY_FILL_FRAG: &[u8] = include_bytes!("shaders_bin/legacy_fill.frag.spv");
|
||||
pub const LEGACY_TEX_VERT: &[u8] = include_bytes!("shaders_bin/legacy_tex.vert.spv");
|
||||
pub const LEGACY_TEX_FRAG: &[u8] = include_bytes!("shaders_bin/legacy_tex.frag.spv");
|
||||
pub const ROUNDED_FILL_VERT: &[u8] = include_bytes!("shaders_bin/rounded_fill.vert.spv");
|
||||
pub const ROUNDED_FILL_FRAG: &[u8] = include_bytes!("shaders_bin/rounded_fill.frag.spv");
|
||||
pub const ROUNDED_TEX_VERT: &[u8] = include_bytes!("shaders_bin/rounded_tex.vert.spv");
|
||||
pub const ROUNDED_TEX_FRAG: &[u8] = include_bytes!("shaders_bin/rounded_tex.frag.spv");
|
||||
pub const LEGACY_ROUNDED_FILL_VERT: &[u8] =
|
||||
include_bytes!("shaders_bin/legacy_rounded_fill.vert.spv");
|
||||
pub const LEGACY_ROUNDED_FILL_FRAG: &[u8] =
|
||||
include_bytes!("shaders_bin/legacy_rounded_fill.frag.spv");
|
||||
pub const LEGACY_ROUNDED_TEX_VERT: &[u8] =
|
||||
include_bytes!("shaders_bin/legacy_rounded_tex.vert.spv");
|
||||
pub const LEGACY_ROUNDED_TEX_FRAG: &[u8] =
|
||||
include_bytes!("shaders_bin/legacy_rounded_tex.frag.spv");
|
||||
|
||||
pub struct VulkanShader {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
|
|
@ -99,6 +111,69 @@ pub struct LegacyTexPushConstants {
|
|||
|
||||
unsafe impl Packed for LegacyTexPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RoundedFillPushConstants {
|
||||
pub color: [f32; 4],
|
||||
pub vertices: DeviceAddress,
|
||||
pub size: [f32; 2],
|
||||
pub corner_radius: [f32; 4],
|
||||
pub border_width: f32,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
unsafe impl Packed for RoundedFillPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LegacyRoundedFillPushConstants {
|
||||
pub pos: [[f32; 2]; 4],
|
||||
pub color: [f32; 4],
|
||||
pub size_x: f32,
|
||||
pub size_y: f32,
|
||||
pub corner_radius_tl: f32,
|
||||
pub corner_radius_tr: f32,
|
||||
pub corner_radius_br: f32,
|
||||
pub corner_radius_bl: f32,
|
||||
pub border_width: f32,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
unsafe impl Packed for LegacyRoundedFillPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RoundedTexPushConstants {
|
||||
pub vertices: DeviceAddress,
|
||||
pub alpha: f32,
|
||||
pub size_x: f32,
|
||||
pub size_y: f32,
|
||||
pub corner_radius_tl: f32,
|
||||
pub corner_radius_tr: f32,
|
||||
pub corner_radius_br: f32,
|
||||
pub corner_radius_bl: f32,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
unsafe impl Packed for RoundedTexPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LegacyRoundedTexPushConstants {
|
||||
pub pos: [[f32; 2]; 4],
|
||||
pub tex_pos: [[f32; 2]; 4],
|
||||
pub alpha: f32,
|
||||
pub size_x: f32,
|
||||
pub size_y: f32,
|
||||
pub corner_radius_tl: f32,
|
||||
pub corner_radius_tr: f32,
|
||||
pub corner_radius_br: f32,
|
||||
pub corner_radius_bl: f32,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
unsafe impl Packed for LegacyRoundedTexPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct OutPushConstants {
|
||||
|
|
|
|||
12
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.common.glsl
Normal file
12
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.common.glsl
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 0) vec2 pos[4];
|
||||
layout(offset = 32) vec4 color;
|
||||
layout(offset = 48) float size_x;
|
||||
layout(offset = 52) float size_y;
|
||||
layout(offset = 56) float corner_radius_tl;
|
||||
layout(offset = 60) float corner_radius_tr;
|
||||
layout(offset = 64) float corner_radius_br;
|
||||
layout(offset = 68) float corner_radius_bl;
|
||||
layout(offset = 72) float border_width;
|
||||
layout(offset = 76) float scale;
|
||||
} data;
|
||||
48
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.frag
Normal file
48
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.frag
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#version 450
|
||||
|
||||
#include "../frag_spec_const.glsl"
|
||||
#include "rounded_fill.common.glsl"
|
||||
|
||||
layout(location = 0) in vec2 geo_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {
|
||||
if (coords.x < 0.0 || coords.y < 0.0 || coords.x > size.x || coords.y > size.y) {
|
||||
return 0.0;
|
||||
}
|
||||
vec2 center;
|
||||
float radius;
|
||||
if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
|
||||
radius = corner_radius.x;
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.y && coords.y < corner_radius.y) {
|
||||
radius = corner_radius.y;
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.z && coords.y > size.y - corner_radius.z) {
|
||||
radius = corner_radius.z;
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else if (coords.x < corner_radius.w && coords.y > size.y - corner_radius.w) {
|
||||
radius = corner_radius.w;
|
||||
center = vec2(radius, size.y - radius);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
float half_px = 0.5 / data.scale;
|
||||
float dist = distance(coords, center);
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
vec4 corner_radius = vec4(data.corner_radius_tl, data.corner_radius_tr, data.corner_radius_br, data.corner_radius_bl);
|
||||
float outer_alpha = rounding_alpha(geo_pos, size, corner_radius);
|
||||
float alpha = outer_alpha;
|
||||
if (data.border_width > 0.0) {
|
||||
vec2 inner_coords = geo_pos - vec2(data.border_width);
|
||||
vec2 inner_size = size - vec2(2.0 * data.border_width);
|
||||
vec4 inner_radius = max(corner_radius - vec4(data.border_width), vec4(0.0));
|
||||
float inner_alpha = rounding_alpha(inner_coords, inner_size, inner_radius);
|
||||
alpha = outer_alpha * (1.0 - inner_alpha);
|
||||
}
|
||||
out_color = data.color * alpha;
|
||||
}
|
||||
17
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.vert
Normal file
17
src/gfx_apis/vulkan/shaders/legacy/rounded_fill.vert
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#version 450
|
||||
|
||||
#include "rounded_fill.common.glsl"
|
||||
|
||||
layout(location = 0) out vec2 geo_pos;
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
vec2 pos;
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: pos = data.pos[0]; geo_pos = vec2(size.x, 0.0); break;
|
||||
case 1: pos = data.pos[1]; geo_pos = vec2(0.0, 0.0); break;
|
||||
case 2: pos = data.pos[2]; geo_pos = vec2(size.x, size.y); break;
|
||||
case 3: pos = data.pos[3]; geo_pos = vec2(0.0, size.y); break;
|
||||
}
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
12
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.common.glsl
Normal file
12
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.common.glsl
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 0) vec2 pos[4];
|
||||
layout(offset = 32) vec2 tex_pos[4];
|
||||
layout(offset = 64) float mul;
|
||||
layout(offset = 68) float size_x;
|
||||
layout(offset = 72) float size_y;
|
||||
layout(offset = 76) float corner_radius_tl;
|
||||
layout(offset = 80) float corner_radius_tr;
|
||||
layout(offset = 84) float corner_radius_br;
|
||||
layout(offset = 88) float corner_radius_bl;
|
||||
layout(offset = 92) float scale;
|
||||
} data;
|
||||
51
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.frag
Normal file
51
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.frag
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#version 450
|
||||
|
||||
#include "../frag_spec_const.glsl"
|
||||
#include "rounded_tex.common.glsl"
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D tex;
|
||||
layout(location = 0) in vec2 tex_pos;
|
||||
layout(location = 1) in vec2 geo_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {
|
||||
if (coords.x < 0.0 || coords.y < 0.0 || coords.x > size.x || coords.y > size.y) {
|
||||
return 0.0;
|
||||
}
|
||||
vec2 center;
|
||||
float radius;
|
||||
if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
|
||||
radius = corner_radius.x;
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.y && coords.y < corner_radius.y) {
|
||||
radius = corner_radius.y;
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.z && coords.y > size.y - corner_radius.z) {
|
||||
radius = corner_radius.z;
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else if (coords.x < corner_radius.w && coords.y > size.y - corner_radius.w) {
|
||||
radius = corner_radius.w;
|
||||
center = vec2(radius, size.y - radius);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
float half_px = 0.5 / data.scale;
|
||||
float dist = distance(coords, center);
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
vec4 corner_radius = vec4(data.corner_radius_tl, data.corner_radius_tr, data.corner_radius_br, data.corner_radius_bl);
|
||||
vec4 c = textureLod(tex, tex_pos, 0);
|
||||
if (has_alpha_multiplier) {
|
||||
if (src_has_alpha) {
|
||||
c *= data.mul;
|
||||
} else {
|
||||
c = vec4(c.rgb * data.mul, data.mul);
|
||||
}
|
||||
}
|
||||
float ra = rounding_alpha(geo_pos, size, corner_radius);
|
||||
c *= ra;
|
||||
out_color = c;
|
||||
}
|
||||
18
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.vert
Normal file
18
src/gfx_apis/vulkan/shaders/legacy/rounded_tex.vert
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#version 450
|
||||
|
||||
#include "rounded_tex.common.glsl"
|
||||
|
||||
layout(location = 0) out vec2 tex_pos;
|
||||
layout(location = 1) out vec2 geo_pos;
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
vec2 pos;
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: pos = data.pos[0]; tex_pos = data.tex_pos[0]; geo_pos = vec2(size.x, 0.0); break;
|
||||
case 1: pos = data.pos[1]; tex_pos = data.tex_pos[1]; geo_pos = vec2(0.0, 0.0); break;
|
||||
case 2: pos = data.pos[2]; tex_pos = data.tex_pos[2]; geo_pos = vec2(size.x, size.y); break;
|
||||
case 3: pos = data.pos[3]; tex_pos = data.tex_pos[3]; geo_pos = vec2(0.0, size.y); break;
|
||||
}
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
14
src/gfx_apis/vulkan/shaders/rounded_fill.common.glsl
Normal file
14
src/gfx_apis/vulkan/shaders/rounded_fill.common.glsl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
layout(buffer_reference, buffer_reference_align = 8, std430) readonly buffer Vertices {
|
||||
vec2 pos[][4];
|
||||
};
|
||||
|
||||
layout(push_constant, std430) uniform Data {
|
||||
vec4 color;
|
||||
Vertices vertices;
|
||||
vec2 size;
|
||||
vec4 corner_radius;
|
||||
float border_width;
|
||||
float scale;
|
||||
} data;
|
||||
45
src/gfx_apis/vulkan/shaders/rounded_fill.frag
Normal file
45
src/gfx_apis/vulkan/shaders/rounded_fill.frag
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#version 450
|
||||
|
||||
#include "rounded_fill.common.glsl"
|
||||
|
||||
layout(location = 0) in vec2 geo_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {
|
||||
if (coords.x < 0.0 || coords.y < 0.0 || coords.x > size.x || coords.y > size.y) {
|
||||
return 0.0;
|
||||
}
|
||||
vec2 center;
|
||||
float radius;
|
||||
if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
|
||||
radius = corner_radius.x;
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.y && coords.y < corner_radius.y) {
|
||||
radius = corner_radius.y;
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.z && coords.y > size.y - corner_radius.z) {
|
||||
radius = corner_radius.z;
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else if (coords.x < corner_radius.w && coords.y > size.y - corner_radius.w) {
|
||||
radius = corner_radius.w;
|
||||
center = vec2(radius, size.y - radius);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
float half_px = 0.5 / data.scale;
|
||||
float dist = distance(coords, center);
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float outer_alpha = rounding_alpha(geo_pos, data.size, data.corner_radius);
|
||||
float alpha = outer_alpha;
|
||||
if (data.border_width > 0.0) {
|
||||
vec2 inner_coords = geo_pos - vec2(data.border_width);
|
||||
vec2 inner_size = data.size - vec2(2.0 * data.border_width);
|
||||
vec4 inner_radius = max(data.corner_radius - vec4(data.border_width), vec4(0.0));
|
||||
float inner_alpha = rounding_alpha(inner_coords, inner_size, inner_radius);
|
||||
alpha = outer_alpha * (1.0 - inner_alpha);
|
||||
}
|
||||
out_color = data.color * alpha;
|
||||
}
|
||||
16
src/gfx_apis/vulkan/shaders/rounded_fill.vert
Normal file
16
src/gfx_apis/vulkan/shaders/rounded_fill.vert
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#version 450
|
||||
|
||||
#include "rounded_fill.common.glsl"
|
||||
|
||||
layout(location = 0) out vec2 geo_pos;
|
||||
|
||||
void main() {
|
||||
vec2 pos = data.vertices.pos[gl_InstanceIndex][gl_VertexIndex];
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: geo_pos = vec2(data.size.x, 0.0); break;
|
||||
case 1: geo_pos = vec2(0.0, 0.0); break;
|
||||
case 2: geo_pos = vec2(data.size.x, data.size.y); break;
|
||||
case 3: geo_pos = vec2(0.0, data.size.y); break;
|
||||
}
|
||||
}
|
||||
22
src/gfx_apis/vulkan/shaders/rounded_tex.common.glsl
Normal file
22
src/gfx_apis/vulkan/shaders/rounded_tex.common.glsl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
struct Vertex {
|
||||
vec2 pos[4];
|
||||
vec2 tex_pos[4];
|
||||
};
|
||||
|
||||
layout(buffer_reference, buffer_reference_align = 8, std430) readonly buffer Vertices {
|
||||
Vertex vertices[];
|
||||
};
|
||||
|
||||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 0) Vertices vertices;
|
||||
layout(offset = 8) float mul;
|
||||
layout(offset = 12) float size_x;
|
||||
layout(offset = 16) float size_y;
|
||||
layout(offset = 20) float corner_radius_tl;
|
||||
layout(offset = 24) float corner_radius_tr;
|
||||
layout(offset = 28) float corner_radius_br;
|
||||
layout(offset = 32) float corner_radius_bl;
|
||||
layout(offset = 36) float scale;
|
||||
} data;
|
||||
76
src/gfx_apis/vulkan/shaders/rounded_tex.frag
Normal file
76
src/gfx_apis/vulkan/shaders/rounded_tex.frag
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#version 450
|
||||
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
|
||||
#define TEX_SET 1
|
||||
|
||||
#include "frag_spec_const.glsl"
|
||||
#include "rounded_tex.common.glsl"
|
||||
#include "tex_set.glsl"
|
||||
#include "eotfs.glsl"
|
||||
#include "alpha_modes.glsl"
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler sam;
|
||||
layout(location = 0) in vec2 tex_pos;
|
||||
layout(location = 1) in vec2 geo_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
float rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {
|
||||
if (coords.x < 0.0 || coords.y < 0.0 || coords.x > size.x || coords.y > size.y) {
|
||||
return 0.0;
|
||||
}
|
||||
vec2 center;
|
||||
float radius;
|
||||
if (coords.x < corner_radius.x && coords.y < corner_radius.x) {
|
||||
radius = corner_radius.x;
|
||||
center = vec2(radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.y && coords.y < corner_radius.y) {
|
||||
radius = corner_radius.y;
|
||||
center = vec2(size.x - radius, radius);
|
||||
} else if (coords.x > size.x - corner_radius.z && coords.y > size.y - corner_radius.z) {
|
||||
radius = corner_radius.z;
|
||||
center = vec2(size.x - radius, size.y - radius);
|
||||
} else if (coords.x < corner_radius.w && coords.y > size.y - corner_radius.w) {
|
||||
radius = corner_radius.w;
|
||||
center = vec2(radius, size.y - radius);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
float half_px = 0.5 / data.scale;
|
||||
float dist = distance(coords, center);
|
||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
vec4 corner_radius = vec4(data.corner_radius_tl, data.corner_radius_tr, data.corner_radius_br, data.corner_radius_bl);
|
||||
vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0);
|
||||
if (eotf != inv_eotf || has_matrix || alpha_mode != AM_PREMULTIPLIED_ELECTRICAL) {
|
||||
vec3 rgb = c.rgb;
|
||||
if (src_has_alpha && alpha_mode == AM_PREMULTIPLIED_ELECTRICAL) {
|
||||
rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||
}
|
||||
rgb = apply_eotf(rgb);
|
||||
if (src_has_alpha && alpha_mode == AM_PREMULTIPLIED_OPTICAL) {
|
||||
rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||
}
|
||||
if (has_matrix) {
|
||||
rgb = (cm_data.matrix * vec4(rgb, 1.0)).rgb;
|
||||
}
|
||||
rgb = apply_inv_eotf(rgb);
|
||||
if (src_has_alpha) {
|
||||
rgb *= c.a;
|
||||
}
|
||||
c.rgb = rgb;
|
||||
}
|
||||
if (has_alpha_multiplier) {
|
||||
if (src_has_alpha) {
|
||||
c *= data.mul;
|
||||
} else {
|
||||
c = vec4(c.rgb * data.mul, data.mul);
|
||||
}
|
||||
}
|
||||
float ra = rounding_alpha(geo_pos, size, corner_radius);
|
||||
c *= ra;
|
||||
out_color = c;
|
||||
}
|
||||
19
src/gfx_apis/vulkan/shaders/rounded_tex.vert
Normal file
19
src/gfx_apis/vulkan/shaders/rounded_tex.vert
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#version 450
|
||||
|
||||
#include "rounded_tex.common.glsl"
|
||||
|
||||
layout(location = 0) out vec2 tex_pos;
|
||||
layout(location = 1) out vec2 geo_pos;
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(data.size_x, data.size_y);
|
||||
Vertex vertex = data.vertices.vertices[gl_InstanceIndex];
|
||||
gl_Position = vec4(vertex.pos[gl_VertexIndex], 0.0, 1.0);
|
||||
tex_pos = vertex.tex_pos[gl_VertexIndex];
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: geo_pos = vec2(size.x, 0.0); break;
|
||||
case 1: geo_pos = vec2(0.0, 0.0); break;
|
||||
case 2: geo_pos = vec2(size.x, size.y); break;
|
||||
case 3: geo_pos = vec2(0.0, size.y); break;
|
||||
}
|
||||
}
|
||||
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_fill.frag.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_fill.frag.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_fill.vert.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_fill.vert.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_tex.frag.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_tex.frag.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_tex.vert.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/legacy_rounded_tex.vert.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_fill.frag.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_fill.frag.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_fill.vert.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_fill.vert.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_tex.frag.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_tex.frag.spv
Normal file
Binary file not shown.
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_tex.vert.spv
Normal file
BIN
src/gfx_apis/vulkan/shaders_bin/rounded_tex.vert.spv
Normal file
Binary file not shown.
|
|
@ -7,12 +7,24 @@ f93524fd077bc9984702b1e0f92232f80bfe28a0a92439dc164c1ea41fd16d64 src/gfx_apis/vu
|
|||
c315a064b48dd5bdb607a6b79c30d31b6e59ffec69e93d50ab875abf97c41bbf src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl
|
||||
590d061b97446fc501158609eaf098b71bc7b328c008b586ff36613ce690d618 src/gfx_apis/vulkan/shaders/legacy/fill.frag
|
||||
ad22a79e1a88a12daa40c0a2b953084c129a408297c8ca544d60e0b6001470b9 src/gfx_apis/vulkan/shaders/legacy/fill.vert
|
||||
b77838c0aac9ec90ae76cd0d94d3891d72d9a30b09ce77009afd9f4e567dd042 src/gfx_apis/vulkan/shaders/legacy/rounded_fill.common.glsl
|
||||
fa39734aea1c96960f5dc95b999ae2fa5576ecf4b527fd70ee0f643c8ddcc452 src/gfx_apis/vulkan/shaders/legacy/rounded_fill.frag
|
||||
c1914cc00fb4827f65cd55bd0737d159fe44a098a3085a500822fc91cc2bfcad src/gfx_apis/vulkan/shaders/legacy/rounded_fill.vert
|
||||
bd249cf170b72cd833e92a7719e88da0a91e563956579707e693679b443d73d5 src/gfx_apis/vulkan/shaders/legacy/rounded_tex.common.glsl
|
||||
28f3249e0d974a332b2926fb7565930627a093d6ac21ca17f2bf191740d299bd src/gfx_apis/vulkan/shaders/legacy/rounded_tex.frag
|
||||
6ef0bde549dc163cd08f68d975071f5d74213c07ccc4a06b30c6f179b2f848ae src/gfx_apis/vulkan/shaders/legacy/rounded_tex.vert
|
||||
e0a8769dd7938dd02e66db9e9048ed6bef8f8c42671f2e2c7a7976a6d498f685 src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl
|
||||
0e7c72ea11671065842c8b4ad4131a7df33b427dc0ea76bf5a896546f6636cb0 src/gfx_apis/vulkan/shaders/legacy/tex.frag
|
||||
4402f7ccdbb9fb52fb6cda3aab13cf89e2980c79b541f8be0463efd64a5f98ed src/gfx_apis/vulkan/shaders/legacy/tex.vert
|
||||
3ba5d05c2b95099e5424b3ade5d1c31d431f5730b1d0b51a9fb5f8afc4ea14b4 src/gfx_apis/vulkan/shaders/out.common.glsl
|
||||
5069f619c7d722815a022e2d84720a2d8290af49a3ed49ea0cd26b52115cc39a src/gfx_apis/vulkan/shaders/out.frag
|
||||
0adc7e12328c15fb3e7e6c8b8701a182223c2f15337e14131f41dd247e697809 src/gfx_apis/vulkan/shaders/out.vert
|
||||
9202d5c9fc4ce0d5f40ed147f245bd037728c9e060ea46a0f0a1767ca55e6c48 src/gfx_apis/vulkan/shaders/rounded_fill.common.glsl
|
||||
9085625d2afb1365685ae79a58108bf6566573ed94d9913397cf74dc6ef9b6e8 src/gfx_apis/vulkan/shaders/rounded_fill.frag
|
||||
7665319a706e514f125d80f51f10b643f01cdae54d8a6ea56c218f78de7c0ecb src/gfx_apis/vulkan/shaders/rounded_fill.vert
|
||||
dd100d048c0b380c913cffd7ac48fed3a341b3cb052302a11c369967f38aba9a src/gfx_apis/vulkan/shaders/rounded_tex.common.glsl
|
||||
454f34754ea4102190821c2d168dedd8c6bf624f1712b6136d902428f801a1e9 src/gfx_apis/vulkan/shaders/rounded_tex.frag
|
||||
21b18ba369b505b9aedb8cf2e7e31bc417f6704fd2daac353b0db52f9ae44c70 src/gfx_apis/vulkan/shaders/rounded_tex.vert
|
||||
e22d4d3318a350def8ef19c7b27dc6a308a84c2fe9d7c02b81107f72073cd481 src/gfx_apis/vulkan/shaders/tex.common.glsl
|
||||
1f196cee646a934072beb3e5648a5042c035953d9a0c26b0a22e330c2f8bb994 src/gfx_apis/vulkan/shaders/tex.frag
|
||||
423cf327c9fcc4070dbf75321c1224a1589b6cf3d2f1ea5e8bd0362e1a9f3aa1 src/gfx_apis/vulkan/shaders/tex.vert
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#![allow(clippy::excessive_precision)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use {
|
||||
crate::{
|
||||
|
|
@ -22,12 +23,14 @@ pub struct Icons {
|
|||
icons: CopyHashMap<i32, Option<Rc<SizedIcons>>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug, Linearize)]
|
||||
pub enum IconState {
|
||||
Active,
|
||||
Passive,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct SizedIcons {
|
||||
pub pin_unfocused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
||||
pub pin_focused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
||||
|
|
|
|||
|
|
@ -1320,6 +1320,7 @@ impl WlSeatGlobal {
|
|||
.start_drag(self, origin, source, icon, serial)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start_tile_drag(self: &Rc<Self>, tl: &Rc<dyn ToplevelNode>) {
|
||||
if self.state.ui_drag_enabled.get() {
|
||||
self.pointer_owner.start_tile_drag(self, tl);
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ impl PointerOwnerHolder {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start_tile_drag(&self, seat: &Rc<WlSeatGlobal>, tl: &Rc<dyn ToplevelNode>) {
|
||||
self.owner.get().start_tile_drag(seat, tl);
|
||||
}
|
||||
|
|
@ -288,6 +289,7 @@ trait PointerOwner {
|
|||
fn disable_window_management(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
let _ = seat;
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn start_tile_drag(&self, seat: &Rc<WlSeatGlobal>, tl: &Rc<dyn ToplevelNode>) {
|
||||
let _ = seat;
|
||||
let _ = tl;
|
||||
|
|
@ -732,6 +734,7 @@ trait SimplePointerOwnerUsecase: Sized + Clone + 'static {
|
|||
let _ = seat;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn start_tile_drag(
|
||||
&self,
|
||||
grab: &SimpleGrabPointerOwner<Self>,
|
||||
|
|
|
|||
|
|
@ -528,6 +528,8 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
GfxApiOpt::Sync => {}
|
||||
GfxApiOpt::FillRect(f) => fill_rect(&f, staging),
|
||||
GfxApiOpt::CopyTexture(c) => copy_texture(&c, staging)?,
|
||||
GfxApiOpt::RoundedFillRect(_) => {}
|
||||
GfxApiOpt::RoundedCopyTexture(_) => {}
|
||||
}
|
||||
}
|
||||
copy_from_staging(staging);
|
||||
|
|
|
|||
328
src/renderer.rs
328
src/renderer.rs
|
|
@ -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,37 +306,10 @@ 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() {
|
||||
let cr = self.state.theme.corner_radius.get();
|
||||
let full_h = mb.y2();
|
||||
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),
|
||||
|
|
@ -393,6 +317,25 @@ impl Renderer<'_> {
|
|||
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();
|
||||
if !child.node.node_is_container() && gap != 0 {
|
||||
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()),
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
} 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
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,7 +595,27 @@ 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) {
|
||||
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);
|
||||
|
|
@ -664,6 +637,7 @@ impl Renderer<'_> {
|
|||
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],
|
||||
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,
|
||||
);
|
||||
}
|
||||
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,
|
||||
None,
|
||||
x,
|
||||
y,
|
||||
None,
|
||||
None,
|
||||
self.base.scale,
|
||||
Some(&bounds),
|
||||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ use {
|
|||
},
|
||||
gfx_api::{
|
||||
AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt,
|
||||
GfxTexture, ReleaseSync, SampleRect,
|
||||
GfxTexture, ReleaseSync, RoundedCopyTexture, RoundedFillRect, SampleRect,
|
||||
},
|
||||
rect::Rect,
|
||||
scale::Scale,
|
||||
theme::Color,
|
||||
theme::{Color, CornerRadius},
|
||||
tree::Transform,
|
||||
},
|
||||
std::rc::Rc,
|
||||
|
|
@ -250,6 +250,123 @@ impl RendererBase<'_> {
|
|||
}));
|
||||
}
|
||||
|
||||
pub fn fill_rounded_rect(
|
||||
&mut self,
|
||||
rect: Rect,
|
||||
color: &Color,
|
||||
alpha: Option<f32>,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
render_intent: RenderIntent,
|
||||
corner_radius: CornerRadius,
|
||||
border_width: f32,
|
||||
) {
|
||||
if *color == Color::TRANSPARENT {
|
||||
return;
|
||||
}
|
||||
let rect = self.scale_rect(rect);
|
||||
let width = (rect.x2() - rect.x1()) as f32;
|
||||
let height = (rect.y2() - rect.y1()) as f32;
|
||||
let scale = self.scalef as f32;
|
||||
let fitted = corner_radius.fit_to(width, height);
|
||||
let cr: [f32; 4] = fitted.into();
|
||||
self.ops
|
||||
.push(GfxApiOpt::RoundedFillRect(RoundedFillRect {
|
||||
rect: FramebufferRect::new(
|
||||
rect.x1() as f32,
|
||||
rect.y1() as f32,
|
||||
rect.x2() as f32,
|
||||
rect.y2() as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
),
|
||||
color: *color,
|
||||
alpha,
|
||||
render_intent,
|
||||
cd: cd.clone(),
|
||||
size: [width, height],
|
||||
corner_radius: cr,
|
||||
border_width,
|
||||
scale,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn render_rounded_texture(
|
||||
&mut self,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
alpha: Option<f32>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
tpoints: Option<SampleRect>,
|
||||
tsize: Option<(i32, i32)>,
|
||||
tscale: Scale,
|
||||
bounds: Option<&Rect>,
|
||||
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
render_intent: RenderIntent,
|
||||
alpha_mode: AlphaMode,
|
||||
corner_radius: CornerRadius,
|
||||
) {
|
||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||
|
||||
let (twidth, theight) = if let Some(size) = tsize {
|
||||
size
|
||||
} else {
|
||||
let (mut w, mut h) = texcoord.buffer_transform.maybe_swap(texture.size());
|
||||
if tscale != self.scale {
|
||||
let tscale = tscale.to_f64();
|
||||
w = (w as f64 * self.scalef / tscale).round() as _;
|
||||
h = (h as f64 * self.scalef / tscale).round() as _;
|
||||
}
|
||||
(w, h)
|
||||
};
|
||||
|
||||
let mut target_x = [x, x + twidth];
|
||||
let mut target_y = [y, y + theight];
|
||||
|
||||
if let Some(bounds) = bounds
|
||||
&& bound_target(&mut target_x, &mut target_y, &mut texcoord, bounds)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let target = FramebufferRect::new(
|
||||
target_x[0] as f32,
|
||||
target_y[0] as f32,
|
||||
target_x[1] as f32,
|
||||
target_y[1] as f32,
|
||||
self.transform,
|
||||
self.fb_width,
|
||||
self.fb_height,
|
||||
);
|
||||
|
||||
let width = (target_x[1] - target_x[0]) as f32;
|
||||
let height = (target_y[1] - target_y[0]) as f32;
|
||||
let scale = self.scalef as f32;
|
||||
let fitted = corner_radius.fit_to(width, height);
|
||||
let cr: [f32; 4] = fitted.into();
|
||||
|
||||
self.ops
|
||||
.push(GfxApiOpt::RoundedCopyTexture(RoundedCopyTexture {
|
||||
tex: texture.clone(),
|
||||
source: texcoord,
|
||||
target,
|
||||
alpha,
|
||||
buffer_resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
opaque: false,
|
||||
render_intent,
|
||||
cd: cd.clone(),
|
||||
alpha_mode,
|
||||
size: [width, height],
|
||||
corner_radius: cr,
|
||||
scale,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn sync(&mut self) {
|
||||
self.ops.push(GfxApiOpt::Sync);
|
||||
}
|
||||
|
|
|
|||
32
src/state.rs
32
src/state.rs
|
|
@ -189,10 +189,8 @@ pub struct State {
|
|||
pub theme: Theme,
|
||||
pub pending_container_layout: AsyncQueue<Rc<ContainerNode>>,
|
||||
pub pending_container_render_positions: AsyncQueue<Rc<ContainerNode>>,
|
||||
pub pending_container_render_title: AsyncQueue<Rc<ContainerNode>>,
|
||||
pub pending_output_render_data: AsyncQueue<Rc<OutputNode>>,
|
||||
pub pending_float_layout: AsyncQueue<Rc<FloatNode>>,
|
||||
pub pending_float_titles: AsyncQueue<Rc<FloatNode>>,
|
||||
pub pending_input_popup_positioning: AsyncQueue<Rc<ZwpInputPopupSurfaceV2>>,
|
||||
pub pending_toplevel_screencasts: AsyncQueue<Rc<JayScreencast>>,
|
||||
pub pending_screencast_reallocs_or_reconfigures: AsyncQueue<Rc<JayScreencast>>,
|
||||
|
|
@ -275,6 +273,7 @@ pub struct State {
|
|||
pub color_manager: Rc<ColorManager>,
|
||||
pub float_above_fullscreen: Cell<bool>,
|
||||
pub icons: Icons,
|
||||
#[allow(dead_code)]
|
||||
pub show_pin_icon: Cell<bool>,
|
||||
pub cl_matcher_manager: ClMatcherManager,
|
||||
pub tl_matcher_manager: TlMatcherManager,
|
||||
|
|
@ -550,10 +549,6 @@ impl DrmDevData {
|
|||
struct UpdateTextTexturesVisitor;
|
||||
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||
node.children
|
||||
.iter()
|
||||
.for_each(|c| c.title_tex.borrow_mut().clear());
|
||||
node.schedule_render_titles();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||
|
|
@ -561,8 +556,6 @@ impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
|||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||
node.title_textures.borrow_mut().clear();
|
||||
node.schedule_render_titles();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||
|
|
@ -685,10 +678,6 @@ impl State {
|
|||
struct Walker;
|
||||
impl NodeVisitorBase for Walker {
|
||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||
node.render_data.borrow_mut().titles.clear();
|
||||
node.children
|
||||
.iter()
|
||||
.for_each(|c| c.title_tex.borrow_mut().clear());
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||
|
|
@ -702,7 +691,6 @@ impl State {
|
|||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||
node.title_textures.borrow_mut().clear();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||
|
|
@ -1118,10 +1106,8 @@ impl State {
|
|||
self.dbus.clear();
|
||||
self.pending_container_layout.clear();
|
||||
self.pending_container_render_positions.clear();
|
||||
self.pending_container_render_title.clear();
|
||||
self.pending_output_render_data.clear();
|
||||
self.pending_float_layout.clear();
|
||||
self.pending_float_titles.clear();
|
||||
self.pending_input_popup_positioning.clear();
|
||||
self.pending_toplevel_screencasts.clear();
|
||||
self.pending_screencast_reallocs_or_reconfigures.clear();
|
||||
|
|
@ -1296,8 +1282,8 @@ impl State {
|
|||
let (width, height) = target.logical_size(target_transform);
|
||||
Rect::new_sized_saturating(0, 0, width, height)
|
||||
},
|
||||
icons: None,
|
||||
stretch: None,
|
||||
corner_radius: None,
|
||||
};
|
||||
let mut sample_rect = SampleRect::identity();
|
||||
sample_rect.buffer_transform = transform;
|
||||
|
|
@ -1621,8 +1607,7 @@ impl State {
|
|||
.unwrap_or(c);
|
||||
Some(target.predict_child_body_size())
|
||||
} else {
|
||||
let tpuh = self.theme.title_plus_underline_height();
|
||||
Some((pos.width(), (pos.height() - tpuh).max(0)))
|
||||
Some((pos.width(), pos.height()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1863,11 +1848,13 @@ impl State {
|
|||
self.spaces_changed();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_show_titles(&self, show: bool) {
|
||||
self.theme.show_titles.set(show);
|
||||
self.spaces_changed();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_floating_titles(&self, floating: bool) {
|
||||
self.theme.floating_titles.set(floating);
|
||||
self.spaces_changed();
|
||||
|
|
@ -1882,13 +1869,9 @@ impl State {
|
|||
.set(threshold.saturating_mul(threshold));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_show_pin_icon(&self, show: bool) {
|
||||
self.show_pin_icon.set(show);
|
||||
for stacked in self.root.stacked.iter() {
|
||||
if let Some(float) = stacked.deref().clone().node_into_float() {
|
||||
float.schedule_render_titles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_float_above_fullscreen(&self, v: bool) {
|
||||
|
|
@ -1909,7 +1892,6 @@ impl State {
|
|||
struct V;
|
||||
impl NodeVisitorBase for V {
|
||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||
node.schedule_render_titles();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||
|
|
@ -1917,7 +1899,6 @@ impl State {
|
|||
node.node_visit_children(self);
|
||||
}
|
||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||
node.schedule_render_titles();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
}
|
||||
|
|
@ -1943,6 +1924,7 @@ impl State {
|
|||
self.fonts_changed();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_title_font(&self, font: Option<&str>) {
|
||||
let font = font.map(|font| Arc::new(font.to_string()));
|
||||
self.theme.title_font.set(font);
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ impl Shared {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(PartialEq, Default)]
|
||||
enum Config<'a> {
|
||||
#[default]
|
||||
|
|
@ -519,6 +520,7 @@ impl TextTexture {
|
|||
self.data.pending_render.set(Some(pending));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn schedule_render(
|
||||
&self,
|
||||
on_completed: Rc<dyn OnCompleted>,
|
||||
|
|
|
|||
122
src/theme.rs
122
src/theme.rs
|
|
@ -643,6 +643,95 @@ impl Into<ConfigBarPosition> for BarPosition {
|
|||
}
|
||||
}
|
||||
|
||||
/// Per-corner radius for rounded rectangles.
|
||||
///
|
||||
/// Each field specifies the radius (in logical pixels) for one corner.
|
||||
/// A radius of 0 means a square corner.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct CornerRadius {
|
||||
pub top_left: f32,
|
||||
pub top_right: f32,
|
||||
pub bottom_right: f32,
|
||||
pub bottom_left: f32,
|
||||
}
|
||||
|
||||
impl From<f32> for CornerRadius {
|
||||
fn from(value: f32) -> Self {
|
||||
Self {
|
||||
top_left: value,
|
||||
top_right: value,
|
||||
bottom_right: value,
|
||||
bottom_left: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CornerRadius> for [f32; 4] {
|
||||
fn from(cr: CornerRadius) -> Self {
|
||||
[cr.top_left, cr.top_right, cr.bottom_right, cr.bottom_left]
|
||||
}
|
||||
}
|
||||
|
||||
impl CornerRadius {
|
||||
/// Shrink or grow all radii by `width`. Radii that are 0 stay 0 (square
|
||||
/// corners remain square). Negative `width` shrinks; the result is clamped
|
||||
/// to 0.
|
||||
pub fn expanded_by(mut self, width: f32) -> Self {
|
||||
if self.top_left > 0.0 {
|
||||
self.top_left = (self.top_left + width).max(0.0);
|
||||
}
|
||||
if self.top_right > 0.0 {
|
||||
self.top_right = (self.top_right + width).max(0.0);
|
||||
}
|
||||
if self.bottom_right > 0.0 {
|
||||
self.bottom_right = (self.bottom_right + width).max(0.0);
|
||||
}
|
||||
if self.bottom_left > 0.0 {
|
||||
self.bottom_left = (self.bottom_left + width).max(0.0);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Scale all radii by a factor (e.g. for HiDPI).
|
||||
pub fn scaled_by(self, scale: f32) -> Self {
|
||||
Self {
|
||||
top_left: self.top_left * scale,
|
||||
top_right: self.top_right * scale,
|
||||
bottom_right: self.bottom_right * scale,
|
||||
bottom_left: self.bottom_left * scale,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduce all radii proportionally so that adjacent corners don't overlap,
|
||||
/// following the CSS spec algorithm.
|
||||
pub fn fit_to(self, width: f32, height: f32) -> Self {
|
||||
let reduction = f32::min(
|
||||
f32::min(
|
||||
width / (self.top_left + self.top_right),
|
||||
width / (self.bottom_left + self.bottom_right),
|
||||
),
|
||||
f32::min(
|
||||
height / (self.top_left + self.bottom_left),
|
||||
height / (self.top_right + self.bottom_right),
|
||||
),
|
||||
);
|
||||
let reduction = f32::min(1.0, reduction);
|
||||
Self {
|
||||
top_left: self.top_left * reduction,
|
||||
top_right: self.top_right * reduction,
|
||||
bottom_right: self.bottom_right * reduction,
|
||||
bottom_left: self.bottom_left * reduction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.top_left == 0.0
|
||||
&& self.top_right == 0.0
|
||||
&& self.bottom_right == 0.0
|
||||
&& self.bottom_left == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Theme {
|
||||
pub colors: ThemeColors,
|
||||
pub sizes: ThemeSizes,
|
||||
|
|
@ -650,9 +739,12 @@ pub struct Theme {
|
|||
pub bar_font: CloneCell<Option<Arc<String>>>,
|
||||
pub title_font: CloneCell<Option<Arc<String>>>,
|
||||
pub default_font: Arc<String>,
|
||||
#[allow(dead_code)]
|
||||
pub show_titles: Cell<bool>,
|
||||
#[allow(dead_code)]
|
||||
pub floating_titles: Cell<bool>,
|
||||
pub bar_position: Cell<BarPosition>,
|
||||
pub corner_radius: Cell<CornerRadius>,
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
|
|
@ -668,51 +760,23 @@ impl Default for Theme {
|
|||
show_titles: Cell::new(true),
|
||||
floating_titles: Cell::new(false),
|
||||
bar_position: Default::default(),
|
||||
corner_radius: Cell::new(CornerRadius::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
pub fn title_font(&self) -> Arc<String> {
|
||||
self.title_font.get().unwrap_or_else(|| self.font.get())
|
||||
}
|
||||
|
||||
pub fn bar_font(&self) -> Arc<String> {
|
||||
self.bar_font.get().unwrap_or_else(|| self.font.get())
|
||||
}
|
||||
|
||||
pub fn title_height(&self) -> i32 {
|
||||
if self.show_titles.get() {
|
||||
self.sizes.title_height.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title_underline_height(&self) -> i32 {
|
||||
if self.show_titles.get() { 1 } else { 0 }
|
||||
}
|
||||
|
||||
pub fn floating_title_top_margin(&self) -> i32 {
|
||||
if self.floating_titles.get() && self.sizes.gap.get() != 0 {
|
||||
self.sizes.border_width.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title_plus_underline_height(&self) -> i32 {
|
||||
if self.show_titles.get() {
|
||||
if self.floating_titles.get() && self.sizes.gap.get() != 0 {
|
||||
let bw = self.sizes.border_width.get();
|
||||
3 * bw + self.sizes.title_height.get() + self.sizes.title_gap.get()
|
||||
} else {
|
||||
self.sizes.title_height.get() + 1
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -4,38 +4,29 @@ use {
|
|||
cursor::KnownCursor,
|
||||
cursor_user::CursorUser,
|
||||
fixed::Fixed,
|
||||
gfx_api::GfxTexture,
|
||||
ifs::wl_seat::{
|
||||
BTN_LEFT, BTN_RIGHT, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci,
|
||||
BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci,
|
||||
collect_kb_foci2,
|
||||
tablet::{TabletTool, TabletToolChanges, TabletToolId},
|
||||
wl_pointer::PendingScroll,
|
||||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
text::TextTexture,
|
||||
tree::{
|
||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FloatNode, FoundNode, Node,
|
||||
NodeId, NodeLayerLink, NodeLocation, OutputNode, TddType, TileDragDestination,
|
||||
ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelType, WorkspaceNode,
|
||||
default_tile_drag_bounds, toplevel_set_floating, toplevel_set_workspace,
|
||||
default_tile_drag_bounds, toplevel_set_workspace,
|
||||
walker::NodeVisitor,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
clonecell::CloneCell,
|
||||
double_click_state::DoubleClickState,
|
||||
errorfmt::ErrorFmt,
|
||||
event_listener::LazyEventSource,
|
||||
hash_map_ext::HashMapExt,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
on_drop_event::OnDropEvent,
|
||||
rc_eq::rc_eq,
|
||||
scroller::Scroller,
|
||||
smallmap::SmallMapMut,
|
||||
threshold_counter::ThresholdCounter,
|
||||
},
|
||||
},
|
||||
|
|
@ -94,20 +85,9 @@ pub enum ContainerFocus {
|
|||
|
||||
tree_id!(ContainerNodeId);
|
||||
|
||||
pub struct ContainerTitle {
|
||||
pub rect: Rect,
|
||||
pub tex: Rc<dyn GfxTexture>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ContainerRenderData {
|
||||
pub title_rects: Vec<Rect>,
|
||||
pub active_title_rects: Vec<Rect>,
|
||||
pub attention_title_rects: Vec<Rect>,
|
||||
pub last_active_rect: Option<Rect>,
|
||||
pub border_rects: Vec<Rect>,
|
||||
pub underline_rects: Vec<Rect>,
|
||||
pub titles: SmallMapMut<Scale, Vec<ContainerTitle>, 2>,
|
||||
}
|
||||
|
||||
pub struct ContainerNode {
|
||||
|
|
@ -123,9 +103,8 @@ pub struct ContainerNode {
|
|||
pub content_width: Cell<i32>,
|
||||
pub content_height: Cell<i32>,
|
||||
pub sum_factors: Cell<f64>,
|
||||
layout_scheduled: Cell<bool>,
|
||||
pub layout_scheduled: Cell<bool>,
|
||||
compute_render_positions_scheduled: Cell<bool>,
|
||||
render_titles_scheduled: Cell<bool>,
|
||||
num_children: NumCell<usize>,
|
||||
pub children: LinkedList<ContainerChild>,
|
||||
focus_history: LinkedList<NodeRef<ContainerChild>>,
|
||||
|
|
@ -135,7 +114,6 @@ pub struct ContainerNode {
|
|||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||
state: Rc<State>,
|
||||
pub render_data: RefCell<ContainerRenderData>,
|
||||
scroller: Scroller,
|
||||
toplevel_data: ToplevelData,
|
||||
attention_requests: ThresholdCounter,
|
||||
pub layout_complete: Rc<LazyEventSource>,
|
||||
|
|
@ -154,9 +132,6 @@ pub struct ContainerChild {
|
|||
pub node: Rc<dyn ToplevelNode>,
|
||||
pub active: Cell<bool>,
|
||||
pub attention_requested: Cell<bool>,
|
||||
title: RefCell<String>,
|
||||
pub title_tex: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
||||
pub title_rect: Cell<Rect>,
|
||||
focus_history: Cell<Option<LinkedNode<NodeRef<ContainerChild>>>>,
|
||||
|
||||
// fields below only valid in tabbed layout
|
||||
|
|
@ -178,7 +153,6 @@ struct CursorState {
|
|||
x: i32,
|
||||
y: i32,
|
||||
op: Option<SeatOp>,
|
||||
double_click_state: DoubleClickState,
|
||||
}
|
||||
|
||||
impl ContainerChild {
|
||||
|
|
@ -212,9 +186,6 @@ impl ContainerNode {
|
|||
body: Default::default(),
|
||||
content: Default::default(),
|
||||
factor: Cell::new(1.0),
|
||||
title: Default::default(),
|
||||
title_tex: Default::default(),
|
||||
title_rect: Default::default(),
|
||||
focus_history: Default::default(),
|
||||
attention_requested: Cell::new(false),
|
||||
border_color_is_focused: Default::default(),
|
||||
|
|
@ -238,7 +209,6 @@ impl ContainerNode {
|
|||
sum_factors: Cell::new(1.0),
|
||||
layout_scheduled: Cell::new(false),
|
||||
compute_render_positions_scheduled: Cell::new(false),
|
||||
render_titles_scheduled: Cell::new(false),
|
||||
num_children: NumCell::new(1),
|
||||
children,
|
||||
focus_history: Default::default(),
|
||||
|
|
@ -248,7 +218,6 @@ impl ContainerNode {
|
|||
cursors: RefCell::new(Default::default()),
|
||||
state: state.clone(),
|
||||
render_data: Default::default(),
|
||||
scroller: Default::default(),
|
||||
toplevel_data: ToplevelData::new(
|
||||
state,
|
||||
Default::default(),
|
||||
|
|
@ -339,9 +308,6 @@ impl ContainerNode {
|
|||
|
||||
content: Default::default(),
|
||||
factor: Default::default(),
|
||||
title: Default::default(),
|
||||
title_tex: Default::default(),
|
||||
title_rect: Default::default(),
|
||||
focus_history: Default::default(),
|
||||
attention_requested: Default::default(),
|
||||
border_color_is_focused: Default::default(),
|
||||
|
|
@ -384,35 +350,25 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
pub fn predict_child_body_size(&self) -> (i32, i32) {
|
||||
let tpuh = self.state.theme.title_plus_underline_height();
|
||||
if self.mono_child.is_some() {
|
||||
let mb = self.mono_body.get();
|
||||
return (mb.width(), mb.height());
|
||||
}
|
||||
let gap = self.state.theme.sizes.gap.get();
|
||||
let bw = self.state.theme.sizes.border_width.get();
|
||||
let floating = self.state.theme.floating_titles.get() && gap != 0;
|
||||
let nc = self.num_children.get() as i32 + 1;
|
||||
match self.split.get() {
|
||||
ContainerSplit::Horizontal => {
|
||||
let spacing = if floating {
|
||||
self.state.theme.sizes.title_gap.get() + 2 * bw
|
||||
} else {
|
||||
gap.max(bw)
|
||||
};
|
||||
let spacing = gap.max(bw);
|
||||
let content_w = self.width.get().sub((nc - 1) * spacing).max(0);
|
||||
(content_w / nc, self.height.get().sub(tpuh).max(0))
|
||||
(content_w / nc, self.height.get())
|
||||
}
|
||||
ContainerSplit::Vertical => {
|
||||
let spacing = if floating {
|
||||
self.state.theme.sizes.title_gap.get() + bw
|
||||
} else {
|
||||
gap.max(bw)
|
||||
};
|
||||
let spacing = gap.max(bw);
|
||||
let content_h = self
|
||||
.height
|
||||
.get()
|
||||
.sub(tpuh + (nc - 1) * (spacing + tpuh))
|
||||
.sub((nc - 1) * spacing)
|
||||
.max(0);
|
||||
(self.width.get(), content_h / nc)
|
||||
}
|
||||
|
|
@ -427,8 +383,6 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
pub fn on_colors_changed(self: &Rc<Self>) {
|
||||
// log::info!("on_colors_changed");
|
||||
self.schedule_render_titles();
|
||||
self.schedule_compute_render_positions();
|
||||
}
|
||||
|
||||
|
|
@ -487,7 +441,6 @@ impl ContainerNode {
|
|||
}
|
||||
self.state.tree_changed();
|
||||
// log::info!("perform_layout");
|
||||
self.schedule_render_titles();
|
||||
self.schedule_compute_render_positions();
|
||||
self.layout_complete.trigger();
|
||||
if self.all_children_match_body() {
|
||||
|
|
@ -507,51 +460,14 @@ impl ContainerNode {
|
|||
.tl_change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get()));
|
||||
self.mono_content
|
||||
.set(child.content.get().at_point(mb.x1(), mb.y1()));
|
||||
|
||||
let th = self.state.theme.title_height();
|
||||
let bw = self.state.theme.sizes.border_width.get();
|
||||
let bw_top = self.state.theme.floating_title_top_margin();
|
||||
let floating = self.state.theme.floating_titles.get();
|
||||
let spacing = if floating {
|
||||
self.state.theme.sizes.title_gap.get() + 2 * bw
|
||||
} else {
|
||||
bw
|
||||
};
|
||||
let num_children = self.num_children.get() as i32;
|
||||
let content_width = self.width.get().sub(spacing * (num_children - 1)).max(0);
|
||||
let width_per_child = content_width / num_children;
|
||||
let mut rem = content_width % num_children;
|
||||
let mut pos = 0;
|
||||
for child in self.children.iter() {
|
||||
let mut width = width_per_child;
|
||||
if rem > 0 {
|
||||
width += 1;
|
||||
rem -= 1;
|
||||
}
|
||||
child
|
||||
.title_rect
|
||||
.set(Rect::new_sized_saturating(pos, bw_top, width, th));
|
||||
pos += width + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_split_layout(self: &Rc<Self>) {
|
||||
let sum_factors = self.sum_factors.get();
|
||||
let gap = self.state.theme.sizes.gap.get();
|
||||
let border_width = self.state.theme.sizes.border_width.get();
|
||||
let floating = self.state.theme.floating_titles.get() && gap != 0;
|
||||
let title_height_tmp = self.state.theme.title_height();
|
||||
let tpuh = self.state.theme.title_plus_underline_height();
|
||||
let split = self.split.get();
|
||||
let spacing = if floating {
|
||||
let title_gap = self.state.theme.sizes.title_gap.get();
|
||||
match split {
|
||||
ContainerSplit::Horizontal => title_gap + 2 * border_width,
|
||||
ContainerSplit::Vertical => title_gap + border_width,
|
||||
}
|
||||
} else {
|
||||
gap.max(border_width)
|
||||
};
|
||||
let spacing = gap.max(border_width);
|
||||
let (content_size, other_content_size) = match split {
|
||||
ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()),
|
||||
ContainerSplit::Vertical => (self.content_height.get(), self.content_width.get()),
|
||||
|
|
@ -569,15 +485,12 @@ impl ContainerNode {
|
|||
body_size = body_size.min(remaining_content_size);
|
||||
remaining_content_size -= body_size;
|
||||
let (x1, y1, width, height) = match split {
|
||||
ContainerSplit::Horizontal => (pos, tpuh, body_size, other_content_size),
|
||||
_ => (0, pos + tpuh, other_content_size, body_size),
|
||||
ContainerSplit::Horizontal => (pos, 0, body_size, other_content_size),
|
||||
_ => (0, pos, other_content_size, body_size),
|
||||
};
|
||||
let body = Rect::new_sized_saturating(x1, y1, width, height);
|
||||
child.body.set(body);
|
||||
pos += body_size + spacing;
|
||||
if split == ContainerSplit::Vertical {
|
||||
pos += tpuh;
|
||||
}
|
||||
}
|
||||
if remaining_content_size > 0 {
|
||||
let size_per = remaining_content_size / num_children as i32;
|
||||
|
|
@ -593,31 +506,21 @@ impl ContainerNode {
|
|||
let (x1, y1, width, height, size) = match split {
|
||||
ContainerSplit::Horizontal => {
|
||||
let width = body.width() + add;
|
||||
(pos, tpuh, width, other_content_size, width)
|
||||
(pos, 0, width, other_content_size, width)
|
||||
}
|
||||
_ => {
|
||||
let height = body.height() + add;
|
||||
(0, pos + tpuh, other_content_size, height, height)
|
||||
(0, pos, other_content_size, height, height)
|
||||
}
|
||||
};
|
||||
body = Rect::new_sized_saturating(x1, y1, width, height);
|
||||
child.body.set(body);
|
||||
pos += size + spacing;
|
||||
if split == ContainerSplit::Vertical {
|
||||
pos += tpuh;
|
||||
}
|
||||
}
|
||||
}
|
||||
let bw_top = self.state.theme.floating_title_top_margin();
|
||||
self.sum_factors.set(1.0);
|
||||
for child in self.children.iter() {
|
||||
let body = child.body.get();
|
||||
child.title_rect.set(Rect::new_sized_saturating(
|
||||
body.x1(),
|
||||
body.y1() - tpuh + bw_top,
|
||||
body.width(),
|
||||
title_height_tmp,
|
||||
));
|
||||
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
|
||||
child.node.clone().tl_change_extents(&body);
|
||||
child.position_content();
|
||||
|
|
@ -627,31 +530,20 @@ impl ContainerNode {
|
|||
fn update_content_size(&self) {
|
||||
let gap = self.state.theme.sizes.gap.get();
|
||||
let border_width = self.state.theme.sizes.border_width.get();
|
||||
let floating = self.state.theme.floating_titles.get() && gap != 0;
|
||||
let title_gap = self.state.theme.sizes.title_gap.get();
|
||||
let tpuh = self.state.theme.title_plus_underline_height();
|
||||
let nc = self.num_children.get();
|
||||
match self.split.get() {
|
||||
ContainerSplit::Horizontal => {
|
||||
let spacing = if floating {
|
||||
title_gap + 2 * border_width
|
||||
} else {
|
||||
gap.max(border_width)
|
||||
};
|
||||
let spacing = gap.max(border_width);
|
||||
let new_content_size = self.width.get().sub((nc - 1) as i32 * spacing).max(0);
|
||||
self.content_width.set(new_content_size);
|
||||
self.content_height.set(self.height.get().sub(tpuh).max(0));
|
||||
self.content_height.set(self.height.get());
|
||||
}
|
||||
ContainerSplit::Vertical => {
|
||||
let spacing = if floating {
|
||||
title_gap + border_width
|
||||
} else {
|
||||
gap.max(border_width)
|
||||
};
|
||||
let spacing = gap.max(border_width);
|
||||
let new_content_size = self
|
||||
.height
|
||||
.get()
|
||||
.sub(tpuh + (nc - 1) as i32 * (spacing + tpuh))
|
||||
.sub((nc - 1) as i32 * spacing)
|
||||
.max(0);
|
||||
self.content_height.set(new_content_size);
|
||||
self.content_width.set(self.width.get());
|
||||
|
|
@ -659,15 +551,15 @@ impl ContainerNode {
|
|||
}
|
||||
self.mono_body.set(Rect::new_sized_saturating(
|
||||
0,
|
||||
tpuh,
|
||||
0,
|
||||
self.width.get(),
|
||||
self.height.get() - tpuh,
|
||||
self.height.get(),
|
||||
));
|
||||
}
|
||||
|
||||
fn pointer_move(
|
||||
self: &Rc<Self>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
id: CursorType,
|
||||
cursor: &CursorUser,
|
||||
x: Fixed,
|
||||
|
|
@ -676,7 +568,6 @@ impl ContainerNode {
|
|||
) {
|
||||
let mut x = x.round_down();
|
||||
let mut y = y.round_down();
|
||||
let title_plus_underline_height = self.state.theme.title_plus_underline_height();
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
||||
cursor: KnownCursor::Default,
|
||||
|
|
@ -684,7 +575,6 @@ impl ContainerNode {
|
|||
x,
|
||||
y,
|
||||
op: None,
|
||||
double_click_state: Default::default(),
|
||||
});
|
||||
let mut changed = false;
|
||||
changed |= mem::replace(&mut seat_state.x, x) != x;
|
||||
|
|
@ -694,15 +584,6 @@ impl ContainerNode {
|
|||
}
|
||||
if let Some(op) = &seat_state.op {
|
||||
match op.kind {
|
||||
SeatOpKind::Move => {
|
||||
if let CursorType::Seat(_) = id
|
||||
&& self.state.ui_drag_threshold_reached((x, y), (op.x, op.y))
|
||||
{
|
||||
let node = op.child.node.clone();
|
||||
drop(seats);
|
||||
seat.start_tile_drag(&node);
|
||||
}
|
||||
}
|
||||
SeatOpKind::Resize {
|
||||
dist_left,
|
||||
dist_right,
|
||||
|
|
@ -748,19 +629,13 @@ impl ContainerNode {
|
|||
let new_cursor = if self.mono_child.is_some() {
|
||||
KnownCursor::Default
|
||||
} else if self.split.get() == ContainerSplit::Horizontal {
|
||||
if y < title_plus_underline_height {
|
||||
KnownCursor::Default
|
||||
} else {
|
||||
KnownCursor::EwResize
|
||||
}
|
||||
} else {
|
||||
let mut cursor = KnownCursor::Default;
|
||||
for child in self.children.iter() {
|
||||
let body = child.body.get();
|
||||
if body.y1() > y {
|
||||
if body.y1() - y > title_plus_underline_height {
|
||||
cursor = KnownCursor::NsResize
|
||||
}
|
||||
cursor = KnownCursor::NsResize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -773,121 +648,6 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_title(&self) {
|
||||
let mut title = self.toplevel_data.title.borrow_mut();
|
||||
title.clear();
|
||||
let split = match (self.mono_child.is_some(), self.split.get()) {
|
||||
(true, _) => "T",
|
||||
(_, ContainerSplit::Horizontal) => "H",
|
||||
(_, ContainerSplit::Vertical) => "V",
|
||||
};
|
||||
title.push_str(split);
|
||||
title.push_str("[");
|
||||
for (i, c) in self.children.iter().enumerate() {
|
||||
if i > 0 {
|
||||
title.push_str(", ");
|
||||
}
|
||||
title.push_str(c.title.borrow_mut().deref());
|
||||
}
|
||||
title.push_str("]");
|
||||
drop(title);
|
||||
self.tl_title_changed();
|
||||
}
|
||||
|
||||
pub fn schedule_render_titles(self: &Rc<Self>) {
|
||||
if !self.render_titles_scheduled.replace(true) {
|
||||
self.state.pending_container_render_title.push(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn last_active(&self) -> Option<NodeId> {
|
||||
match self.mono_child.get() {
|
||||
Some(c) => Some(c.node.node_id()),
|
||||
None => self.focus_history.last().map(|v| v.node.node_id()),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_titles(&self) -> Rc<AsyncEvent> {
|
||||
let on_completed = Rc::new(OnDropEvent::default());
|
||||
let Some(ctx) = self.state.render_ctx.get() else {
|
||||
return on_completed.event();
|
||||
};
|
||||
let theme = &self.state.theme;
|
||||
let th = theme.title_height();
|
||||
let font = theme.title_font();
|
||||
let last_active = self.last_active();
|
||||
let have_active = self.children.iter().any(|c| c.active.get());
|
||||
let scales = self.state.scales.lock();
|
||||
for child in self.children.iter() {
|
||||
let rect = child.title_rect.get();
|
||||
let color = if child.active.get() {
|
||||
theme.colors.focused_title_text.get()
|
||||
} else if child.attention_requested.get() {
|
||||
theme.colors.unfocused_title_text.get()
|
||||
} else if !have_active && last_active == Some(child.node.node_id()) {
|
||||
theme.colors.focused_inactive_title_text.get()
|
||||
} else {
|
||||
theme.colors.unfocused_title_text.get()
|
||||
};
|
||||
let title = child.title.borrow_mut();
|
||||
let tt = &mut *child.title_tex.borrow_mut();
|
||||
for (scale, _) in scales.iter() {
|
||||
let tex = tt.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx));
|
||||
let mut th = th;
|
||||
let mut scalef = None;
|
||||
let mut width = rect.width();
|
||||
if *scale != 1 {
|
||||
let scale = scale.to_f64();
|
||||
th = (th as f64 * scale).round() as _;
|
||||
width = (width as f64 * scale).round() as _;
|
||||
scalef = Some(scale);
|
||||
}
|
||||
tex.schedule_render(
|
||||
on_completed.clone(),
|
||||
1,
|
||||
None,
|
||||
width,
|
||||
th,
|
||||
1,
|
||||
&font,
|
||||
title.deref(),
|
||||
color,
|
||||
true,
|
||||
false,
|
||||
scalef,
|
||||
);
|
||||
}
|
||||
}
|
||||
on_completed.event()
|
||||
}
|
||||
|
||||
fn compute_title_data(&self) {
|
||||
let rd = &mut *self.render_data.borrow_mut();
|
||||
for (_, v) in rd.titles.iter_mut() {
|
||||
v.clear();
|
||||
}
|
||||
let abs_x = self.abs_x1.get();
|
||||
let abs_y = self.abs_y1.get();
|
||||
for child in self.children.iter() {
|
||||
let rect = child.title_rect.get();
|
||||
if self.toplevel_data.visible.get() {
|
||||
self.state.damage(rect.move_(abs_x, abs_y));
|
||||
}
|
||||
let title = child.title.borrow_mut();
|
||||
let tt = &*child.title_tex.borrow();
|
||||
for (scale, tex) in tt {
|
||||
if let Err(e) = tex.flip() {
|
||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||
}
|
||||
if let Some(tex) = tex.texture() {
|
||||
let titles = rd.titles.get_or_default_mut(*scale);
|
||||
titles.push(ContainerTitle { rect, tex })
|
||||
}
|
||||
}
|
||||
}
|
||||
rd.titles.remove_if(|_, v| v.is_empty());
|
||||
}
|
||||
|
||||
fn schedule_compute_render_positions(self: &Rc<Self>) {
|
||||
if !self.compute_render_positions_scheduled.replace(true) {
|
||||
self.state
|
||||
|
|
@ -901,110 +661,36 @@ impl ContainerNode {
|
|||
let mut rd = self.render_data.borrow_mut();
|
||||
let rd = rd.deref_mut();
|
||||
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 cwidth = self.width.get();
|
||||
let cheight = self.height.get();
|
||||
for (_, v) in rd.titles.iter_mut() {
|
||||
v.clear();
|
||||
}
|
||||
rd.title_rects.clear();
|
||||
rd.active_title_rects.clear();
|
||||
rd.attention_title_rects.clear();
|
||||
rd.border_rects.clear();
|
||||
rd.underline_rects.clear();
|
||||
rd.last_active_rect.take();
|
||||
let last_active = self.last_active();
|
||||
let mono = self.mono_child.is_some();
|
||||
let split = self.split.get();
|
||||
let have_active = self.children.iter().any(|c| c.active.get());
|
||||
let abs_x = self.abs_x1.get();
|
||||
let abs_y = self.abs_y1.get();
|
||||
let gap = self.state.theme.sizes.gap.get();
|
||||
let floating_titles = self.state.theme.floating_titles.get();
|
||||
let title_bw = if gap != 0 && !floating_titles { bw } else { 0 };
|
||||
for (i, child) in self.children.iter().enumerate() {
|
||||
let rect = child.title_rect.get();
|
||||
if self.toplevel_data.visible.get() && !mono && split != ContainerSplit::Horizontal {
|
||||
self.state.damage(Rect::new_sized_saturating(
|
||||
abs_x - title_bw,
|
||||
abs_y + rect.y1(),
|
||||
cwidth + 2 * title_bw,
|
||||
rect.height() + tuh,
|
||||
));
|
||||
}
|
||||
if gap != 0 && !mono && !child.node.node_is_container() {
|
||||
child.border_color_is_focused.set(child.active.get());
|
||||
} else if i > 0 {
|
||||
let body = child.body.get();
|
||||
let sep = if mono {
|
||||
Rect::new_sized_saturating(rect.x1() - bw, 0, bw, th)
|
||||
// In mono mode, no separators needed without title tabs
|
||||
continue;
|
||||
} else if split == ContainerSplit::Horizontal {
|
||||
Rect::new_sized_saturating(rect.x1() - bw, 0, bw, cheight)
|
||||
Rect::new_sized_saturating(body.x1() - bw, 0, bw, cheight)
|
||||
} else {
|
||||
Rect::new_sized_saturating(0, rect.y1() - bw, cwidth, bw)
|
||||
Rect::new_sized_saturating(0, body.y1() - bw, cwidth, bw)
|
||||
};
|
||||
if gap == 0 || (mono && !floating_titles) {
|
||||
if gap == 0 {
|
||||
rd.border_rects.push(sep);
|
||||
}
|
||||
}
|
||||
if child.active.get() {
|
||||
rd.active_title_rects.push(rect);
|
||||
} else if child.attention_requested.get() {
|
||||
rd.attention_title_rects.push(rect);
|
||||
} else if !have_active && last_active == Some(child.node.node_id()) {
|
||||
rd.last_active_rect = Some(rect);
|
||||
} else {
|
||||
rd.title_rects.push(rect);
|
||||
}
|
||||
if !mono && (!floating_titles || gap == 0) {
|
||||
let rect = Rect::new_sized_saturating(rect.x1(), rect.y2(), rect.width(), 1);
|
||||
rd.underline_rects.push(rect);
|
||||
if self.toplevel_data.visible.get() {
|
||||
self.state.damage(Rect::new_sized_saturating(abs_x, abs_y, cwidth, cheight));
|
||||
}
|
||||
let tt = &*child.title_tex.borrow();
|
||||
for (scale, tex) in tt {
|
||||
if let Some(tex) = tex.texture() {
|
||||
let titles = rd.titles.get_or_default_mut(*scale);
|
||||
titles.push(ContainerTitle { rect, tex })
|
||||
}
|
||||
}
|
||||
}
|
||||
if mono && (!floating_titles || gap == 0) {
|
||||
rd.underline_rects
|
||||
.push(Rect::new_sized_saturating(0, th, cwidth, tuh));
|
||||
}
|
||||
if gap == 0 && th > 0 {
|
||||
rd.underline_rects
|
||||
.push(Rect::new_sized_saturating(0, 0, cwidth, tuh));
|
||||
}
|
||||
if title_bw > 0 {
|
||||
let extend = |r: Rect| {
|
||||
let x1 = if r.x1() == 0 { -title_bw } else { r.x1() };
|
||||
let x2 = if r.x2() == cwidth { r.x2() + title_bw } else { r.x2() };
|
||||
Rect::new_sized_saturating(x1, r.y1(), x2 - x1, r.height())
|
||||
};
|
||||
for r in &mut rd.title_rects {
|
||||
*r = extend(*r);
|
||||
}
|
||||
for r in &mut rd.active_title_rects {
|
||||
*r = extend(*r);
|
||||
}
|
||||
for r in &mut rd.attention_title_rects {
|
||||
*r = extend(*r);
|
||||
}
|
||||
if let Some(r) = &mut rd.last_active_rect {
|
||||
*r = extend(*r);
|
||||
}
|
||||
for r in &mut rd.underline_rects {
|
||||
*r = extend(*r);
|
||||
}
|
||||
}
|
||||
if self.toplevel_data.visible.get() && (mono || split == ContainerSplit::Horizontal) {
|
||||
self.state
|
||||
.damage(Rect::new_sized_saturating(abs_x - title_bw, abs_y, cwidth + 2 * title_bw, tpuh));
|
||||
}
|
||||
rd.titles.remove_if(|_, v| v.is_empty());
|
||||
}
|
||||
|
||||
fn activate_child(self: &Rc<Self>, child: &NodeRef<ContainerChild>) {
|
||||
|
|
@ -1081,7 +767,6 @@ impl ContainerNode {
|
|||
self.mono_child.set(child);
|
||||
// log::info!("set_mono");
|
||||
self.schedule_layout();
|
||||
self.update_title();
|
||||
}
|
||||
|
||||
pub fn set_split(self: &Rc<Self>, split: ContainerSplit) {
|
||||
|
|
@ -1089,7 +774,6 @@ impl ContainerNode {
|
|||
self.update_content_size();
|
||||
// log::info!("set_split");
|
||||
self.schedule_layout();
|
||||
self.update_title();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1279,20 +963,6 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_child_title(self: &Rc<Self>, child: &ContainerChild, title: &str) {
|
||||
{
|
||||
let mut ct = child.title.borrow_mut();
|
||||
if ct.deref() == title {
|
||||
return;
|
||||
}
|
||||
ct.clear();
|
||||
ct.push_str(title);
|
||||
}
|
||||
self.update_title();
|
||||
// log::info!("node_child_title_changed");
|
||||
self.schedule_render_titles();
|
||||
}
|
||||
|
||||
fn update_child_active(
|
||||
self: &Rc<Self>,
|
||||
node: &NodeRef<ContainerChild>,
|
||||
|
|
@ -1307,7 +977,6 @@ impl ContainerNode {
|
|||
.set(Some(self.focus_history.add_last(node.clone())));
|
||||
}
|
||||
// log::info!("node_child_active_changed");
|
||||
self.schedule_render_titles();
|
||||
self.schedule_compute_render_positions();
|
||||
if self.state.theme.sizes.gap.get() != 0 && self.toplevel_data.visible.get() {
|
||||
self.damage();
|
||||
|
|
@ -1338,7 +1007,6 @@ impl ContainerNode {
|
|||
self.mod_attention_requests(true);
|
||||
}
|
||||
}
|
||||
self.update_child_title(child, &data.title.borrow());
|
||||
self.update_child_active(child, data.active(), 1);
|
||||
{
|
||||
let pos = data.pos.get();
|
||||
|
|
@ -1362,19 +1030,11 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn toggle_mono(self: &Rc<Self>) {
|
||||
if self.mono_child.is_some() {
|
||||
self.set_mono(None);
|
||||
} else if let Some(last) = self.focus_history.last() {
|
||||
self.set_mono(Some(&*last.node));
|
||||
}
|
||||
}
|
||||
|
||||
fn button(
|
||||
self: Rc<Self>,
|
||||
id: CursorType,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
time_usec: u64,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
_time_usec: u64,
|
||||
pressed: bool,
|
||||
button: u32,
|
||||
) {
|
||||
|
|
@ -1383,20 +1043,6 @@ impl ContainerNode {
|
|||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if button == BTN_RIGHT && pressed {
|
||||
if self.mono_child.is_some() || self.split.get() == ContainerSplit::Horizontal {
|
||||
if seat_data.y < self.state.theme.title_height() {
|
||||
self.toggle_mono();
|
||||
}
|
||||
} else {
|
||||
for child in self.children.iter() {
|
||||
if child.title_rect.get().contains(seat_data.x, seat_data.y) {
|
||||
self.toggle_mono();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if button != BTN_LEFT {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1407,17 +1053,9 @@ impl ContainerNode {
|
|||
let (kind, child) = 'res: {
|
||||
let mono = self.mono_child.is_some();
|
||||
for child in self.children.iter() {
|
||||
let rect = child.title_rect.get();
|
||||
if rect.contains(seat_data.x, seat_data.y) {
|
||||
self.activate_child(&child);
|
||||
child
|
||||
.node
|
||||
.clone()
|
||||
.node_do_focus(seat, Direction::Unspecified);
|
||||
break 'res (SeatOpKind::Move, child);
|
||||
} else if !mono {
|
||||
if !mono {
|
||||
if self.split.get() == ContainerSplit::Horizontal {
|
||||
if seat_data.x < rect.x1() {
|
||||
if seat_data.x < child.body.get().x1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.x
|
||||
|
|
@ -1428,7 +1066,7 @@ impl ContainerNode {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
if seat_data.y < rect.y1() {
|
||||
if seat_data.y < child.body.get().y1() {
|
||||
break 'res (
|
||||
SeatOpKind::Resize {
|
||||
dist_left: seat_data.y
|
||||
|
|
@ -1443,20 +1081,9 @@ impl ContainerNode {
|
|||
}
|
||||
return;
|
||||
};
|
||||
if seat_data
|
||||
.double_click_state
|
||||
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||
&& kind == SeatOpKind::Move
|
||||
{
|
||||
drop(seat_datas);
|
||||
toplevel_set_floating(&self.state, child.node.clone(), true);
|
||||
return;
|
||||
}
|
||||
seat_data.op = Some(SeatOp {
|
||||
child,
|
||||
kind,
|
||||
x: seat_data.x,
|
||||
y: seat_data.y,
|
||||
})
|
||||
} else if !pressed {
|
||||
seat_data.op = None;
|
||||
|
|
@ -1464,65 +1091,6 @@ impl ContainerNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn tile_drag_destination_mono_titles(
|
||||
self: &Rc<Self>,
|
||||
source: NodeId,
|
||||
abs_bounds: Rect,
|
||||
abs_x: i32,
|
||||
abs_y: i32,
|
||||
) -> Option<TileDragDestination> {
|
||||
let mut prev_is_source = false;
|
||||
let mut prev_center = 0;
|
||||
for child in self.children.iter() {
|
||||
if child.node.node_id() == source {
|
||||
prev_is_source = true;
|
||||
continue;
|
||||
}
|
||||
let rect = child.title_rect.get();
|
||||
let center = (rect.x1() + rect.x2()) / 2;
|
||||
if !prev_is_source {
|
||||
let rect = Rect::new(prev_center, 0, center, rect.height())?
|
||||
.move_(self.abs_x1.get(), self.abs_y1.get())
|
||||
.intersect(abs_bounds);
|
||||
if rect.contains(abs_x, abs_y) {
|
||||
return Some(TileDragDestination {
|
||||
highlight: rect,
|
||||
ty: TddType::Insert {
|
||||
container: self.clone(),
|
||||
neighbor: child.node.clone(),
|
||||
before: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
prev_center = center;
|
||||
prev_is_source = false;
|
||||
}
|
||||
if prev_is_source {
|
||||
return None;
|
||||
}
|
||||
let last = self.children.last()?;
|
||||
let rect = Rect::new(
|
||||
prev_center,
|
||||
0,
|
||||
self.width.get(),
|
||||
self.state.theme.title_height(),
|
||||
)?
|
||||
.move_(self.abs_x1.get(), self.abs_y1.get())
|
||||
.intersect(abs_bounds);
|
||||
if rect.contains(abs_x, abs_y) {
|
||||
return Some(TileDragDestination {
|
||||
highlight: rect,
|
||||
ty: TddType::Insert {
|
||||
container: self.clone(),
|
||||
neighbor: last.node.clone(),
|
||||
before: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn tile_drag_destination_mono(
|
||||
self: &Rc<Self>,
|
||||
mc: &ContainerChild,
|
||||
|
|
@ -1531,10 +1099,6 @@ impl ContainerNode {
|
|||
abs_x: i32,
|
||||
abs_y: i32,
|
||||
) -> Option<TileDragDestination> {
|
||||
let th = self.state.theme.title_height();
|
||||
if abs_y < self.abs_y1.get() + th {
|
||||
return self.tile_drag_destination_mono_titles(source, abs_bounds, abs_x, abs_y);
|
||||
}
|
||||
let body = self.mono_body.get();
|
||||
let mut bounds = body
|
||||
.move_(self.abs_x1.get(), self.abs_y1.get())
|
||||
|
|
@ -1696,13 +1260,10 @@ impl ContainerNode {
|
|||
struct SeatOp {
|
||||
child: NodeRef<ContainerChild>,
|
||||
kind: SeatOpKind,
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum SeatOpKind {
|
||||
Move,
|
||||
Resize { dist_left: i32, dist_right: i32 },
|
||||
}
|
||||
|
||||
|
|
@ -1724,17 +1285,6 @@ pub async fn container_render_positions(state: Rc<State>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn container_render_titles(state: Rc<State>) {
|
||||
loop {
|
||||
let container = state.pending_container_render_title.pop().await;
|
||||
if container.render_titles_scheduled.get() {
|
||||
container.render_titles_scheduled.set(false);
|
||||
container.render_titles().triggered().await;
|
||||
container.compute_title_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ContainerNode {
|
||||
fn node_id(&self) -> NodeId {
|
||||
self.id.into()
|
||||
|
|
@ -1779,10 +1329,8 @@ impl Node for ContainerNode {
|
|||
self.toplevel_data.node_layer()
|
||||
}
|
||||
|
||||
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
||||
if let Some(child) = self.child_nodes.borrow().get(&child.node_id()) {
|
||||
self.update_child_title(child, title);
|
||||
}
|
||||
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, _title: &str) {
|
||||
// Titlebars removed; no title tracking needed
|
||||
}
|
||||
|
||||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
||||
|
|
@ -1885,41 +1433,8 @@ impl Node for ContainerNode {
|
|||
self.button(id, seat, time_usec, state == ButtonState::Pressed, button);
|
||||
}
|
||||
|
||||
fn node_on_axis_event(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, event: &PendingScroll) {
|
||||
let mut seat_datas = self.cursors.borrow_mut();
|
||||
let id = CursorType::Seat(seat.id());
|
||||
let seat_data = match seat_datas.get_mut(&id) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
if seat_data.y > self.state.theme.title_height() {
|
||||
return;
|
||||
}
|
||||
let cur_mc = match self.mono_child.get() {
|
||||
Some(mc) => mc,
|
||||
_ => return,
|
||||
};
|
||||
let discrete = match self.scroller.handle(event) {
|
||||
Some(d) => d,
|
||||
_ => return,
|
||||
};
|
||||
let mut new_mc = cur_mc.clone();
|
||||
for _ in 0..discrete.abs() {
|
||||
let new = if discrete < 0 {
|
||||
new_mc.prev()
|
||||
} else {
|
||||
new_mc.next()
|
||||
};
|
||||
new_mc = match new {
|
||||
Some(n) => n,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
self.activate_child(&new_mc);
|
||||
new_mc
|
||||
.node
|
||||
.clone()
|
||||
.node_do_focus(seat, Direction::Unspecified);
|
||||
fn node_on_axis_event(self: Rc<Self>, _seat: &Rc<WlSeatGlobal>, _event: &PendingScroll) {
|
||||
// Scroll-to-switch-tabs was a title bar feature; no-op without titles
|
||||
}
|
||||
|
||||
fn node_on_leave(&self, seat: &WlSeatGlobal) {
|
||||
|
|
@ -2051,9 +1566,6 @@ impl ContainingNode for ContainerNode {
|
|||
|
||||
content: Default::default(),
|
||||
factor: Cell::new(node.factor.get()),
|
||||
title: Default::default(),
|
||||
title_tex: Default::default(),
|
||||
title_rect: Cell::new(node.title_rect.get()),
|
||||
focus_history: Cell::new(None),
|
||||
attention_requested: Cell::new(false),
|
||||
border_color_is_focused: Default::default(),
|
||||
|
|
@ -2132,7 +1644,6 @@ impl ContainingNode for ContainerNode {
|
|||
}
|
||||
}
|
||||
self.sum_factors.set(sum);
|
||||
self.update_title();
|
||||
// log::info!("cnode_remove_child2");
|
||||
self.schedule_layout();
|
||||
self.cancel_seat_ops();
|
||||
|
|
@ -2186,9 +1697,8 @@ impl ContainingNode for ContainerNode {
|
|||
let Some(parent) = self.toplevel_data.parent.get() else {
|
||||
return;
|
||||
};
|
||||
let tpuh = self.state.theme.title_plus_underline_height();
|
||||
if self.mono_child.is_some() {
|
||||
parent.cnode_set_child_position(&*self, x, y - tpuh);
|
||||
parent.cnode_set_child_position(&*self, x, y);
|
||||
} else {
|
||||
let children = self.child_nodes.borrow();
|
||||
let Some(child) = children.get(&child.node_id()) else {
|
||||
|
|
@ -2209,7 +1719,6 @@ impl ContainingNode for ContainerNode {
|
|||
new_y2: Option<i32>,
|
||||
) {
|
||||
let theme = &self.state.theme;
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let mut left_outside = false;
|
||||
let mut right_outside = false;
|
||||
|
|
@ -2251,7 +1760,7 @@ impl ContainingNode for ContainerNode {
|
|||
}
|
||||
let (new_delta, between) = match split {
|
||||
ContainerSplit::Horizontal => (self.abs_x1.get(), bw),
|
||||
ContainerSplit::Vertical => (self.abs_y1.get(), bw + tpuh),
|
||||
ContainerSplit::Vertical => (self.abs_y1.get(), bw),
|
||||
};
|
||||
let new_i1 = new_i1.map(|v| v - new_delta);
|
||||
let new_i2 = new_i2.map(|v| v - new_delta);
|
||||
|
|
@ -2319,10 +1828,10 @@ impl ContainingNode for ContainerNode {
|
|||
x2 = new_x2.map(|v| v.max(x1.unwrap_or(pos.x1())));
|
||||
}
|
||||
if top_outside {
|
||||
y1 = new_y1.map(|v| (v - tpuh).min(pos.y2() - tpuh));
|
||||
y1 = new_y1.map(|v| v.min(pos.y2()));
|
||||
}
|
||||
if bottom_outside {
|
||||
y2 = new_y2.map(|v| v.max(y1.unwrap_or(pos.y1()) + tpuh));
|
||||
y2 = new_y2.map(|v| v.max(y1.unwrap_or(pos.y1())));
|
||||
}
|
||||
if ((x1.is_some() && x1 != Some(pos.x1()))
|
||||
|| (x2.is_some() && x2 != Some(pos.x2()))
|
||||
|
|
|
|||
|
|
@ -10,27 +10,22 @@ use {
|
|||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
text::TextTexture,
|
||||
tree::{
|
||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
NodeLayerLink, NodeLocation, OutputNode, PinnedNode, StackedNode, TileDragDestination,
|
||||
ToplevelNode, WorkspaceNode, toplevel_set_floating, walker::NodeVisitor,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, double_click_state::DoubleClickState,
|
||||
errorfmt::ErrorFmt, event_listener::LazyEventSource, linkedlist::LinkedNode,
|
||||
on_drop_event::OnDropEvent, smallmap::SmallMapMut,
|
||||
clonecell::CloneCell, double_click_state::DoubleClickState,
|
||||
event_listener::LazyEventSource, linkedlist::LinkedNode,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
|
@ -50,10 +45,6 @@ pub struct FloatNode {
|
|||
pub active: Cell<bool>,
|
||||
pub seat_state: NodeSeatState,
|
||||
pub layout_scheduled: Cell<bool>,
|
||||
pub render_titles_scheduled: Cell<bool>,
|
||||
pub title_rect: Cell<Rect>,
|
||||
pub title: RefCell<String>,
|
||||
pub title_textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
|
||||
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||
pub attention_requested: Cell<bool>,
|
||||
pub layout_complete: Rc<LazyEventSource>,
|
||||
|
|
@ -99,17 +90,6 @@ pub async fn float_layout(state: Rc<State>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn float_titles(state: Rc<State>) {
|
||||
loop {
|
||||
let node = state.pending_float_titles.pop().await;
|
||||
if node.render_titles_scheduled.get() {
|
||||
node.render_titles_scheduled.set(false);
|
||||
node.render_title_phase1().triggered().await;
|
||||
node.render_title_phase2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FloatNode {
|
||||
pub fn new(
|
||||
state: &Rc<State>,
|
||||
|
|
@ -131,10 +111,6 @@ impl FloatNode {
|
|||
active: Cell::new(false),
|
||||
seat_state: Default::default(),
|
||||
layout_scheduled: Cell::new(false),
|
||||
render_titles_scheduled: Cell::new(false),
|
||||
title_rect: Default::default(),
|
||||
title: Default::default(),
|
||||
title_textures: Default::default(),
|
||||
cursors: Default::default(),
|
||||
attention_requested: Cell::new(false),
|
||||
layout_complete: state.post_layout_event_sources.create_source(),
|
||||
|
|
@ -162,7 +138,7 @@ impl FloatNode {
|
|||
}
|
||||
|
||||
pub fn on_colors_changed(self: &Rc<Self>) {
|
||||
self.schedule_render_titles();
|
||||
// No title rendering needed
|
||||
}
|
||||
|
||||
pub fn schedule_layout(self: &Rc<Self>) {
|
||||
|
|
@ -179,95 +155,17 @@ impl FloatNode {
|
|||
let pos = self.position.get();
|
||||
let theme = &self.state.theme;
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let th = theme.title_height();
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let cpos = Rect::new_sized_saturating(
|
||||
pos.x1() + bw,
|
||||
pos.y1() + bw + tpuh,
|
||||
pos.y1() + bw,
|
||||
pos.width() - 2 * bw,
|
||||
pos.height() - 2 * bw - tpuh,
|
||||
pos.height() - 2 * bw,
|
||||
);
|
||||
let tr = Rect::new_sized_saturating(bw, bw, pos.width() - 2 * bw, th);
|
||||
child.clone().tl_change_extents(&cpos);
|
||||
self.title_rect.set(tr);
|
||||
self.layout_scheduled.set(false);
|
||||
self.schedule_render_titles();
|
||||
self.layout_complete.trigger();
|
||||
}
|
||||
|
||||
pub fn schedule_render_titles(self: &Rc<Self>) {
|
||||
if !self.render_titles_scheduled.replace(true) {
|
||||
self.state.pending_float_titles.push(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn render_title_phase1(&self) -> Rc<AsyncEvent> {
|
||||
let on_completed = Rc::new(OnDropEvent::default());
|
||||
let theme = &self.state.theme;
|
||||
let tc = match self.active.get() {
|
||||
true => theme.colors.focused_title_text.get(),
|
||||
false => theme.colors.unfocused_title_text.get(),
|
||||
};
|
||||
let font = theme.title_font();
|
||||
let title = self.title.borrow_mut();
|
||||
let ctx = match self.state.render_ctx.get() {
|
||||
Some(c) => c,
|
||||
_ => return on_completed.event(),
|
||||
};
|
||||
let scales = self.state.scales.lock();
|
||||
let tr = self.title_rect.get();
|
||||
let tt = &mut *self.title_textures.borrow_mut();
|
||||
for (scale, _) in scales.iter() {
|
||||
let tex = tt.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx));
|
||||
let mut th = tr.height();
|
||||
let mut scalef = None;
|
||||
let mut width = tr.width();
|
||||
if self.state.show_pin_icon.get() || self.pinned_link.borrow().is_some() {
|
||||
width = (width - th).max(0);
|
||||
}
|
||||
if *scale != 1 {
|
||||
let scale = scale.to_f64();
|
||||
th = (th as f64 * scale).round() as _;
|
||||
width = (width as f64 * scale).round() as _;
|
||||
scalef = Some(scale);
|
||||
}
|
||||
tex.schedule_render(
|
||||
on_completed.clone(),
|
||||
1,
|
||||
None,
|
||||
width,
|
||||
th,
|
||||
1,
|
||||
&font,
|
||||
&title,
|
||||
tc,
|
||||
true,
|
||||
false,
|
||||
scalef,
|
||||
);
|
||||
}
|
||||
on_completed.event()
|
||||
}
|
||||
|
||||
fn render_title_phase2(&self) {
|
||||
let theme = &self.state.theme;
|
||||
let th = theme.title_height();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let title = self.title.borrow();
|
||||
let tt = &*self.title_textures.borrow();
|
||||
for (_, tt) in tt {
|
||||
if let Err(e) = tt.flip() {
|
||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
let pos = self.position.get();
|
||||
if self.visible.get() && pos.width() >= 2 * bw {
|
||||
let tr =
|
||||
Rect::new_sized_saturating(pos.x1() + bw, pos.y1() + bw, pos.width() - 2 * bw, th);
|
||||
self.state.damage(tr);
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_move(
|
||||
self: &Rc<Self>,
|
||||
id: CursorType,
|
||||
|
|
@ -280,7 +178,6 @@ impl FloatNode {
|
|||
let y = y.round_down();
|
||||
let theme = &self.state.theme;
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let mut seats = self.cursors.borrow_mut();
|
||||
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
||||
cursor: KnownCursor::Default,
|
||||
|
|
@ -316,7 +213,7 @@ impl FloatNode {
|
|||
}
|
||||
OpType::ResizeTop => {
|
||||
y1 += y - seat_state.dist_ver;
|
||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
||||
y1 = y1.min(y2 - 2 * bw);
|
||||
}
|
||||
OpType::ResizeRight => {
|
||||
x2 += x - pos.width() + seat_state.dist_hor;
|
||||
|
|
@ -324,31 +221,31 @@ impl FloatNode {
|
|||
}
|
||||
OpType::ResizeBottom => {
|
||||
y2 += y - pos.height() + seat_state.dist_ver;
|
||||
y2 = y2.max(y1 + 2 * bw + tpuh);
|
||||
y2 = y2.max(y1 + 2 * bw);
|
||||
}
|
||||
OpType::ResizeTopLeft => {
|
||||
x1 += x - seat_state.dist_hor;
|
||||
y1 += y - seat_state.dist_ver;
|
||||
x1 = x1.min(x2 - 2 * bw);
|
||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
||||
y1 = y1.min(y2 - 2 * bw);
|
||||
}
|
||||
OpType::ResizeTopRight => {
|
||||
x2 += x - pos.width() + seat_state.dist_hor;
|
||||
y1 += y - seat_state.dist_ver;
|
||||
x2 = x2.max(x1 + 2 * bw);
|
||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
||||
y1 = y1.min(y2 - 2 * bw);
|
||||
}
|
||||
OpType::ResizeBottomLeft => {
|
||||
x1 += x - seat_state.dist_hor;
|
||||
y2 += y - pos.height() + seat_state.dist_ver;
|
||||
x1 = x1.min(x2 - 2 * bw);
|
||||
y2 = y2.max(y1 + 2 * bw + tpuh);
|
||||
y2 = y2.max(y1 + 2 * bw);
|
||||
}
|
||||
OpType::ResizeBottomRight => {
|
||||
x2 += x - pos.width() + seat_state.dist_hor;
|
||||
y2 += y - pos.height() + seat_state.dist_ver;
|
||||
x2 = x2.max(x1 + 2 * bw);
|
||||
y2 = y2.max(y1 + 2 * bw + tpuh);
|
||||
y2 = y2.max(y1 + 2 * bw);
|
||||
}
|
||||
}
|
||||
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||
|
|
@ -474,18 +371,12 @@ impl FloatNode {
|
|||
self.schedule_layout();
|
||||
}
|
||||
|
||||
fn update_child_title(self: &Rc<Self>, title: &str) {
|
||||
let mut t = self.title.borrow_mut();
|
||||
if t.deref() != title {
|
||||
t.clear();
|
||||
t.push_str(title);
|
||||
self.schedule_render_titles();
|
||||
}
|
||||
fn update_child_title(self: &Rc<Self>, _title: &str) {
|
||||
// No title rendering
|
||||
}
|
||||
|
||||
fn update_child_active(self: &Rc<Self>, active: bool) {
|
||||
if self.active.replace(active) != active {
|
||||
self.schedule_render_titles();
|
||||
if active {
|
||||
self.restack();
|
||||
}
|
||||
|
|
@ -542,7 +433,6 @@ impl FloatNode {
|
|||
if let Some(tl) = self.child.get() {
|
||||
tl.tl_data().pinned.set(pl.is_some());
|
||||
}
|
||||
self.schedule_render_titles();
|
||||
}
|
||||
|
||||
fn button(
|
||||
|
|
@ -558,34 +448,6 @@ impl FloatNode {
|
|||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
let bw = self.state.theme.sizes.border_width.get();
|
||||
let th = self.state.theme.title_height();
|
||||
let mut is_icon_press = false;
|
||||
if pressed && cursor_data.x >= bw && cursor_data.y >= bw && cursor_data.y < bw + th {
|
||||
enum FloatIcon {
|
||||
Pin,
|
||||
}
|
||||
let mut icons = ArrayVec::<FloatIcon, 1>::new();
|
||||
if self.state.show_pin_icon.get() || self.pinned_link.borrow().is_some() {
|
||||
icons.push(FloatIcon::Pin);
|
||||
}
|
||||
let mut x2 = bw + th;
|
||||
let icon = 'icon: {
|
||||
for icon in icons {
|
||||
if cursor_data.x < x2 {
|
||||
break 'icon Some(icon);
|
||||
}
|
||||
x2 += th;
|
||||
}
|
||||
None
|
||||
};
|
||||
if let Some(icon) = icon {
|
||||
is_icon_press = true;
|
||||
match icon {
|
||||
FloatIcon::Pin => self.toggle_pinned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
if !cursor_data.op_active {
|
||||
if !pressed {
|
||||
return;
|
||||
|
|
@ -601,7 +463,6 @@ impl FloatNode {
|
|||
cursor_data.x,
|
||||
cursor_data.y,
|
||||
) && cursor_data.op_type == OpType::Move
|
||||
&& !is_icon_press
|
||||
&& let Some(tl) = self.child.get()
|
||||
{
|
||||
drop(cursors);
|
||||
|
|
@ -653,11 +514,10 @@ impl FloatNode {
|
|||
let child = self.child.get()?;
|
||||
let theme = &self.state.theme.sizes;
|
||||
let bw = theme.border_width.get();
|
||||
let tpuh = self.state.theme.title_plus_underline_height();
|
||||
let pos = self.position.get();
|
||||
let body = Rect::new(
|
||||
pos.x1() + bw,
|
||||
pos.y1() + bw + tpuh,
|
||||
pos.y1() + bw,
|
||||
pos.x2() - bw,
|
||||
pos.y2() - bw,
|
||||
)?;
|
||||
|
|
@ -713,8 +573,8 @@ impl Node for FloatNode {
|
|||
NodeLayerLink::Stacked(l)
|
||||
}
|
||||
|
||||
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, title: &str) {
|
||||
self.update_child_title(title);
|
||||
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, _title: &str) {
|
||||
// No title rendering
|
||||
}
|
||||
|
||||
fn node_accepts_focus(&self) -> bool {
|
||||
|
|
@ -738,13 +598,12 @@ impl Node for FloatNode {
|
|||
usecase: FindTreeUsecase,
|
||||
) -> FindTreeResult {
|
||||
let theme = &self.state.theme;
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let pos = self.position.get();
|
||||
if x < bw || x >= pos.width() - bw {
|
||||
return FindTreeResult::AcceptsInput;
|
||||
}
|
||||
if y < bw + tpuh || y >= pos.height() - bw {
|
||||
if y < bw || y >= pos.height() - bw {
|
||||
return FindTreeResult::AcceptsInput;
|
||||
}
|
||||
let child = match self.child.get() {
|
||||
|
|
@ -752,7 +611,7 @@ impl Node for FloatNode {
|
|||
_ => return FindTreeResult::Other,
|
||||
};
|
||||
let x = x - bw;
|
||||
let y = y - bw - tpuh;
|
||||
let y = y - bw;
|
||||
tree.push(FoundNode {
|
||||
node: child.clone(),
|
||||
x,
|
||||
|
|
@ -934,9 +793,8 @@ impl ContainingNode for FloatNode {
|
|||
|
||||
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
|
||||
let theme = &self.state.theme;
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let (x, y) = (x - bw, y - tpuh - bw);
|
||||
let (x, y) = (x - bw, y - bw);
|
||||
let pos = self.position.get();
|
||||
if pos.position() != (x, y) {
|
||||
let new_pos = pos.at_point(x, y);
|
||||
|
|
@ -956,7 +814,6 @@ impl ContainingNode for FloatNode {
|
|||
new_y2: Option<i32>,
|
||||
) {
|
||||
let theme = &self.state.theme;
|
||||
let tpuh = theme.title_plus_underline_height();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let pos = self.position.get();
|
||||
let mut x1 = pos.x1();
|
||||
|
|
@ -970,10 +827,10 @@ impl ContainingNode for FloatNode {
|
|||
x2 = (v + bw).max(x1 + bw + bw);
|
||||
}
|
||||
if let Some(v) = new_y1 {
|
||||
y1 = (v - tpuh - bw).min(y2 - bw - tpuh - bw);
|
||||
y1 = (v - bw).min(y2 - bw - bw);
|
||||
}
|
||||
if let Some(v) = new_y2 {
|
||||
y2 = (v + bw).max(y1 + bw + tpuh + bw);
|
||||
y2 = (v + bw).max(y1 + bw + bw);
|
||||
}
|
||||
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||
if new_pos != pos {
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ pub struct Theme {
|
|||
pub gap: Option<i32>,
|
||||
pub floating_titles: Option<bool>,
|
||||
pub title_gap: Option<i32>,
|
||||
pub corner_radius: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
config::{
|
||||
Theme,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, bol, opt, recover, s32, str, val},
|
||||
extractor::{Extractor, ExtractorError, bol, fltorint, opt, recover, s32, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
parsers::color::ColorParser,
|
||||
},
|
||||
|
|
@ -97,7 +97,10 @@ impl Parser for ThemeParser<'_> {
|
|||
recover(opt(bol("floating-titles"))),
|
||||
),
|
||||
))?;
|
||||
let (title_gap,) = ext.extract((recover(opt(s32("title-gap"))),))?;
|
||||
let (title_gap, corner_radius) = ext.extract((
|
||||
recover(opt(s32("title-gap"))),
|
||||
recover(opt(fltorint("corner-radius"))),
|
||||
))?;
|
||||
macro_rules! color {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
|
|
@ -152,6 +155,7 @@ impl Parser for ThemeParser<'_> {
|
|||
gap: gap.despan(),
|
||||
floating_titles: floating_titles.despan(),
|
||||
title_gap: title_gap.despan(),
|
||||
corner_radius: corner_radius.map(|v| v.value as f32),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use {
|
|||
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
||||
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
||||
set_floating_titles, set_middle_click_paste_enabled, set_show_bar, set_show_float_pin_icon,
|
||||
set_show_titles,
|
||||
set_show_titles, set_corner_radius,
|
||||
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||
switch_to_vt,
|
||||
|
|
@ -1020,6 +1020,9 @@ impl State {
|
|||
font!(set_font, font);
|
||||
font!(set_title_font, title_font);
|
||||
font!(set_bar_font, bar_font);
|
||||
if let Some(radius) = theme.corner_radius {
|
||||
set_corner_radius(radius);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_switch_device(self: &Rc<Self>, dev: InputDevice, actions: &Rc<SwitchActions>) {
|
||||
|
|
|
|||
|
|
@ -2426,6 +2426,11 @@ Theme:
|
|||
kind: string
|
||||
required: false
|
||||
description: The name of the font to use in the bar. Defaults to `font` if not set.
|
||||
corner-radius:
|
||||
kind: number
|
||||
minimum: 0
|
||||
required: false
|
||||
description: The corner radius for window borders in logical pixels. Defaults to 0 (square corners).
|
||||
|
||||
|
||||
Config:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue