Compare commits
2 commits
e1928863d9
...
cea4187fc0
| Author | SHA1 | Date | |
|---|---|---|---|
| cea4187fc0 | |||
| a41dbae899 |
57 changed files with 3056 additions and 1048 deletions
|
|
@ -20,10 +20,18 @@ pub const TREES: &[Tree] = &[
|
||||||
"tex.frag",
|
"tex.frag",
|
||||||
"out.vert",
|
"out.vert",
|
||||||
"out.frag",
|
"out.frag",
|
||||||
|
"rounded_fill.frag",
|
||||||
|
"rounded_fill.vert",
|
||||||
|
"rounded_tex.frag",
|
||||||
|
"rounded_tex.vert",
|
||||||
"legacy/fill.frag",
|
"legacy/fill.frag",
|
||||||
"legacy/fill.vert",
|
"legacy/fill.vert",
|
||||||
"legacy/tex.vert",
|
"legacy/tex.vert",
|
||||||
"legacy/tex.frag",
|
"legacy/tex.frag",
|
||||||
|
"legacy/rounded_fill.frag",
|
||||||
|
"legacy/rounded_fill.vert",
|
||||||
|
"legacy/rounded_tex.frag",
|
||||||
|
"legacy/rounded_tex.vert",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2025,6 +2025,52 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::SetPointerRevertKey { seat, key });
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_toggle_expand(&self, seat: Seat) {
|
||||||
|
self.send(&ClientMessage::SeatToggleExpand { seat });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_toggle_tab(&self, seat: Seat) {
|
||||||
|
self.send(&ClientMessage::SeatToggleTab { seat });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_make_group(&self, seat: Seat, axis: Axis, ephemeral: bool) {
|
||||||
|
self.send(&ClientMessage::SeatMakeGroup {
|
||||||
|
seat,
|
||||||
|
axis,
|
||||||
|
ephemeral,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_change_group_opposite(&self, seat: Seat) {
|
||||||
|
self.send(&ClientMessage::SeatChangeGroupOpposite { seat });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_equalize(&self, seat: Seat, recursive: bool) {
|
||||||
|
self.send(&ClientMessage::SeatEqualize { seat, recursive });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_autotile(&self, enabled: bool) {
|
||||||
|
self.send(&ClientMessage::SetAutotile { enabled });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tab_title_align(&self, align: u32) {
|
||||||
|
self.send(&ClientMessage::SetTabTitleAlign { align });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seat_move_tab(&self, seat: Seat, right: bool) {
|
||||||
|
self.send(&ClientMessage::SeatMoveTab { seat, right });
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_msg(&self, msg: &[u8]) {
|
fn handle_msg(&self, msg: &[u8]) {
|
||||||
self.handle_msg2(msg);
|
self.handle_msg2(msg);
|
||||||
self.dispatch_futures();
|
self.dispatch_futures();
|
||||||
|
|
|
||||||
|
|
@ -880,6 +880,38 @@ pub enum ClientMessage<'a> {
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
|
SetCornerRadius {
|
||||||
|
radius: f32,
|
||||||
|
},
|
||||||
|
GetCornerRadius,
|
||||||
|
SeatToggleExpand {
|
||||||
|
seat: Seat,
|
||||||
|
},
|
||||||
|
SeatToggleTab {
|
||||||
|
seat: Seat,
|
||||||
|
},
|
||||||
|
SeatMakeGroup {
|
||||||
|
seat: Seat,
|
||||||
|
axis: Axis,
|
||||||
|
ephemeral: bool,
|
||||||
|
},
|
||||||
|
SeatChangeGroupOpposite {
|
||||||
|
seat: Seat,
|
||||||
|
},
|
||||||
|
SeatEqualize {
|
||||||
|
seat: Seat,
|
||||||
|
recursive: bool,
|
||||||
|
},
|
||||||
|
SetAutotile {
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
|
SetTabTitleAlign {
|
||||||
|
align: u32,
|
||||||
|
},
|
||||||
|
SeatMoveTab {
|
||||||
|
seat: Seat,
|
||||||
|
right: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -1136,6 +1168,9 @@ pub enum Response {
|
||||||
ConnectorSupportsArbitraryModes {
|
ConnectorSupportsArbitraryModes {
|
||||||
supports_arbitrary_modes: bool,
|
supports_arbitrary_modes: bool,
|
||||||
},
|
},
|
||||||
|
GetCornerRadius {
|
||||||
|
radius: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -677,6 +677,33 @@ impl Seat {
|
||||||
pub fn unstable_set_mouse_follows_focus(self, enabled: bool) {
|
pub fn unstable_set_mouse_follows_focus(self, enabled: bool) {
|
||||||
get!().seat_set_mouse_follows_focus(self, enabled)
|
get!().seat_set_mouse_follows_focus(self, enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Toggles tabbed mode on the focused window's parent container.
|
||||||
|
pub fn toggle_tab(self) {
|
||||||
|
get!().seat_toggle_tab(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps the focused child in a new sub-container with the given split axis.
|
||||||
|
pub fn make_group(self, axis: Axis, ephemeral: bool) {
|
||||||
|
get!().seat_make_group(self, axis, ephemeral)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggles the parent container's split direction (H↔V).
|
||||||
|
pub fn change_group_opposite(self) {
|
||||||
|
get!().seat_change_group_opposite(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets all siblings' size factors to equal.
|
||||||
|
pub fn equalize(self, recursive: bool) {
|
||||||
|
get!().seat_equalize(self, recursive)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the active tab left or right within the current tab bar.
|
||||||
|
///
|
||||||
|
/// Equivalent to hy3's `movewindow` within a tabbed group.
|
||||||
|
pub fn move_tab(self, right: bool) {
|
||||||
|
get!().seat_move_tab(self, right)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A focus-follows-mouse mode.
|
/// A focus-follows-mouse mode.
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,42 @@ pub fn toggle_floating_titles() {
|
||||||
get.set_floating_titles(!get.get_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables or disables autotiling.
|
||||||
|
///
|
||||||
|
/// When enabled, new windows are automatically placed in a perpendicular
|
||||||
|
/// sub-container if the predicted body would be narrower than tall (or vice versa).
|
||||||
|
///
|
||||||
|
/// The default is `false`.
|
||||||
|
pub fn set_autotile(enabled: bool) {
|
||||||
|
get!().set_autotile(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the horizontal alignment of title text within tab buttons.
|
||||||
|
///
|
||||||
|
/// - `"start"` — left-aligned (default)
|
||||||
|
/// - `"center"` — centered
|
||||||
|
/// - `"end"` — right-aligned
|
||||||
|
pub fn set_tab_title_align(align: &str) {
|
||||||
|
let val = match align {
|
||||||
|
"center" => 1,
|
||||||
|
"end" => 2,
|
||||||
|
_ => 0, // start
|
||||||
|
};
|
||||||
|
get!().set_tab_title_align(val)
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets a callback to run when this config is unloaded.
|
/// 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
|
/// Only one callback can be set at a time. If another callback is already set, it will be
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,38 @@ pub mod colors {
|
||||||
///
|
///
|
||||||
/// Default: `#9d28c67f`.
|
/// Default: `#9d28c67f`.
|
||||||
const 15 => HIGHLIGHT_COLOR,
|
const 15 => HIGHLIGHT_COLOR,
|
||||||
|
/// The background color of an active (focused) tab.
|
||||||
|
///
|
||||||
|
/// Default: `#33ccff40`.
|
||||||
|
const 16 => TAB_ACTIVE_BACKGROUND_COLOR,
|
||||||
|
/// The border color of an active (focused) tab.
|
||||||
|
///
|
||||||
|
/// Default: `#33ccffee`.
|
||||||
|
const 17 => TAB_ACTIVE_BORDER_COLOR,
|
||||||
|
/// The background color of an inactive tab.
|
||||||
|
///
|
||||||
|
/// Default: `#222222`.
|
||||||
|
const 18 => TAB_INACTIVE_BACKGROUND_COLOR,
|
||||||
|
/// The border color of an inactive tab.
|
||||||
|
///
|
||||||
|
/// Default: `#333333`.
|
||||||
|
const 19 => TAB_INACTIVE_BORDER_COLOR,
|
||||||
|
/// The text color of an active (focused) tab.
|
||||||
|
///
|
||||||
|
/// Default: `#ffffff`.
|
||||||
|
const 20 => TAB_ACTIVE_TEXT_COLOR,
|
||||||
|
/// The text color of an inactive tab.
|
||||||
|
///
|
||||||
|
/// Default: `#888888`.
|
||||||
|
const 21 => TAB_INACTIVE_TEXT_COLOR,
|
||||||
|
/// The background color of the tab bar strip.
|
||||||
|
///
|
||||||
|
/// Default: `#111111`.
|
||||||
|
const 22 => TAB_BAR_BACKGROUND_COLOR,
|
||||||
|
/// The background color of a tab that has requested attention.
|
||||||
|
///
|
||||||
|
/// Default: `#23092c`.
|
||||||
|
const 23 => TAB_ATTENTION_BACKGROUND_COLOR,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the color of GUI element.
|
/// Sets the color of GUI element.
|
||||||
|
|
@ -374,5 +406,29 @@ pub mod sized {
|
||||||
///
|
///
|
||||||
/// Default: 0
|
/// Default: 0
|
||||||
const 06 => TITLE_GAP,
|
const 06 => TITLE_GAP,
|
||||||
|
/// The height of the tab bar in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 22
|
||||||
|
const 07 => TAB_BAR_HEIGHT,
|
||||||
|
/// The padding between tabs in the tab bar in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 6
|
||||||
|
const 08 => TAB_BAR_PADDING,
|
||||||
|
/// The corner radius of tabs in the tab bar in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 6
|
||||||
|
const 09 => TAB_BAR_RADIUS,
|
||||||
|
/// The border width of tabs in the tab bar in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 2
|
||||||
|
const 10 => TAB_BAR_BORDER_WIDTH,
|
||||||
|
/// The horizontal padding within each tab for text in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 4
|
||||||
|
const 11 => TAB_BAR_TEXT_PADDING,
|
||||||
|
/// The gap between the tab bar and the window content below in pixels.
|
||||||
|
///
|
||||||
|
/// Default: 4
|
||||||
|
const 12 => TAB_BAR_GAP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,9 @@ use {
|
||||||
tree::{
|
tree::{
|
||||||
DisplayNode, NodeIds, OutputNode, TearingMode, Transform, VrrMode,
|
DisplayNode, NodeIds, OutputNode, TearingMode, Transform, VrrMode,
|
||||||
WorkspaceDisplayOrder, WorkspaceNode, container_layout, container_render_positions,
|
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,
|
placeholder_render_textures,
|
||||||
|
container_tab_render_textures,
|
||||||
},
|
},
|
||||||
user_session::import_environment,
|
user_session::import_environment,
|
||||||
utils::{
|
utils::{
|
||||||
|
|
@ -259,14 +260,13 @@ fn start_compositor2(
|
||||||
theme: Default::default(),
|
theme: Default::default(),
|
||||||
pending_container_layout: Default::default(),
|
pending_container_layout: Default::default(),
|
||||||
pending_container_render_positions: Default::default(),
|
pending_container_render_positions: Default::default(),
|
||||||
pending_container_render_title: Default::default(),
|
|
||||||
pending_output_render_data: Default::default(),
|
pending_output_render_data: Default::default(),
|
||||||
pending_float_layout: Default::default(),
|
pending_float_layout: Default::default(),
|
||||||
pending_float_titles: Default::default(),
|
|
||||||
pending_input_popup_positioning: Default::default(),
|
pending_input_popup_positioning: Default::default(),
|
||||||
pending_toplevel_screencasts: Default::default(),
|
pending_toplevel_screencasts: Default::default(),
|
||||||
pending_screencast_reallocs_or_reconfigures: Default::default(),
|
pending_screencast_reallocs_or_reconfigures: Default::default(),
|
||||||
pending_placeholder_render_textures: Default::default(),
|
pending_placeholder_render_textures: Default::default(),
|
||||||
|
pending_container_tab_render_textures: Default::default(),
|
||||||
dbus: Dbus::new(&engine, &ring, &run_toplevel),
|
dbus: Dbus::new(&engine, &ring, &run_toplevel),
|
||||||
fdcloser: FdCloser::new(),
|
fdcloser: FdCloser::new(),
|
||||||
logger: logger.clone(),
|
logger: logger.clone(),
|
||||||
|
|
@ -511,16 +511,16 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||||
Phase::PostLayout,
|
Phase::PostLayout,
|
||||||
container_render_positions(state.clone()),
|
container_render_positions(state.clone()),
|
||||||
),
|
),
|
||||||
eng.spawn2(
|
|
||||||
"container titles",
|
|
||||||
Phase::PostLayout,
|
|
||||||
container_render_titles(state.clone()),
|
|
||||||
),
|
|
||||||
eng.spawn2(
|
eng.spawn2(
|
||||||
"placeholder textures",
|
"placeholder textures",
|
||||||
Phase::PostLayout,
|
Phase::PostLayout,
|
||||||
placeholder_render_textures(state.clone()),
|
placeholder_render_textures(state.clone()),
|
||||||
),
|
),
|
||||||
|
eng.spawn2(
|
||||||
|
"container tab textures",
|
||||||
|
Phase::PostLayout,
|
||||||
|
container_tab_render_textures(state.clone()),
|
||||||
|
),
|
||||||
eng.spawn2(
|
eng.spawn2(
|
||||||
"output render",
|
"output render",
|
||||||
Phase::PostLayout,
|
Phase::PostLayout,
|
||||||
|
|
@ -531,11 +531,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
||||||
wlr_output_manager_done(state.clone()),
|
wlr_output_manager_done(state.clone()),
|
||||||
),
|
),
|
||||||
eng.spawn2("float layout", Phase::Layout, float_layout(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("idle", Phase::PostLayout, idle(state.clone())),
|
||||||
eng.spawn2(
|
eng.spawn2(
|
||||||
"input, popup positioning",
|
"input, popup positioning",
|
||||||
|
|
|
||||||
|
|
@ -1411,24 +1411,20 @@ impl ConfigProxyHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_show_titles(&self, show: bool) {
|
fn handle_set_show_titles(&self, _show: bool) {
|
||||||
self.state.set_show_titles(show);
|
// no-op: titles have been removed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_get_show_titles(&self) {
|
fn handle_get_show_titles(&self) {
|
||||||
self.respond(Response::GetShowTitles {
|
self.respond(Response::GetShowTitles { show: false });
|
||||||
show: self.state.theme.show_titles.get(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_floating_titles(&self, floating: bool) {
|
fn handle_set_floating_titles(&self, _floating: bool) {
|
||||||
self.state.set_floating_titles(floating);
|
// no-op: titles have been removed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_get_floating_titles(&self) {
|
fn handle_get_floating_titles(&self) {
|
||||||
self.respond(Response::GetFloatingTitles {
|
self.respond(Response::GetFloatingTitles { floating: false });
|
||||||
floating: self.state.theme.floating_titles.get(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_bar_position(&self, position: BarPosition) -> Result<(), CphError> {
|
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) {
|
fn handle_set_corner_radius(&self, radius: f32) {
|
||||||
self.state.set_show_pin_icon(show);
|
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) {
|
fn handle_set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
|
||||||
|
|
@ -1745,6 +1757,41 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_seat_toggle_tab(&self, seat: Seat) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.toggle_tab();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_make_group(
|
||||||
|
&self,
|
||||||
|
seat: Seat,
|
||||||
|
axis: Axis,
|
||||||
|
ephemeral: bool,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.make_group(axis.into(), ephemeral);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_change_group_opposite(&self, seat: Seat) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.change_group_opposite();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_equalize(&self, seat: Seat, recursive: bool) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.equalize(recursive);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seat_move_tab(&self, seat: Seat, right: bool) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.move_tab(right);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
||||||
let window = self.get_window(window)?;
|
let window = self.get_window(window)?;
|
||||||
self.respond(Response::GetWindowSplit {
|
self.respond(Response::GetWindowSplit {
|
||||||
|
|
@ -2452,6 +2499,12 @@ impl ConfigProxyHandler {
|
||||||
BAR_SEPARATOR_WIDTH => ThemeSized::bar_separator_width,
|
BAR_SEPARATOR_WIDTH => ThemeSized::bar_separator_width,
|
||||||
GAP => ThemeSized::gap,
|
GAP => ThemeSized::gap,
|
||||||
TITLE_GAP => ThemeSized::title_gap,
|
TITLE_GAP => ThemeSized::title_gap,
|
||||||
|
TAB_BAR_HEIGHT => ThemeSized::tab_bar_height,
|
||||||
|
TAB_BAR_PADDING => ThemeSized::tab_bar_padding,
|
||||||
|
TAB_BAR_RADIUS => ThemeSized::tab_bar_radius,
|
||||||
|
TAB_BAR_BORDER_WIDTH => ThemeSized::tab_bar_border_width,
|
||||||
|
TAB_BAR_TEXT_PADDING => ThemeSized::tab_bar_text_padding,
|
||||||
|
TAB_BAR_GAP => ThemeSized::tab_bar_gap,
|
||||||
_ => return Err(CphError::UnknownSized(sized.0)),
|
_ => return Err(CphError::UnknownSized(sized.0)),
|
||||||
};
|
};
|
||||||
Ok(sized)
|
Ok(sized)
|
||||||
|
|
@ -2496,8 +2549,8 @@ impl ConfigProxyHandler {
|
||||||
self.state.set_bar_font(Some(font));
|
self.state.set_bar_font(Some(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_set_title_font(&self, font: &str) {
|
fn handle_set_title_font(&self, _font: &str) {
|
||||||
self.state.set_title_font(Some(font));
|
// no-op: titles have been removed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_get_font(&self) {
|
fn handle_get_font(&self) {
|
||||||
|
|
@ -2529,6 +2582,14 @@ impl ConfigProxyHandler {
|
||||||
BAR_STATUS_TEXT_COLOR => ThemeColor::bar_text,
|
BAR_STATUS_TEXT_COLOR => ThemeColor::bar_text,
|
||||||
ATTENTION_REQUESTED_BACKGROUND_COLOR => ThemeColor::attention_requested_background,
|
ATTENTION_REQUESTED_BACKGROUND_COLOR => ThemeColor::attention_requested_background,
|
||||||
HIGHLIGHT_COLOR => ThemeColor::highlight,
|
HIGHLIGHT_COLOR => ThemeColor::highlight,
|
||||||
|
TAB_ACTIVE_BACKGROUND_COLOR => ThemeColor::tab_active_background,
|
||||||
|
TAB_ACTIVE_BORDER_COLOR => ThemeColor::tab_active_border,
|
||||||
|
TAB_INACTIVE_BACKGROUND_COLOR => ThemeColor::tab_inactive_background,
|
||||||
|
TAB_INACTIVE_BORDER_COLOR => ThemeColor::tab_inactive_border,
|
||||||
|
TAB_ACTIVE_TEXT_COLOR => ThemeColor::tab_active_text,
|
||||||
|
TAB_INACTIVE_TEXT_COLOR => ThemeColor::tab_inactive_text,
|
||||||
|
TAB_BAR_BACKGROUND_COLOR => ThemeColor::tab_bar_background,
|
||||||
|
TAB_ATTENTION_BACKGROUND_COLOR => ThemeColor::tab_attention_background,
|
||||||
_ => return Err(CphError::UnknownColor(colorable.0)),
|
_ => return Err(CphError::UnknownColor(colorable.0)),
|
||||||
};
|
};
|
||||||
Ok(colorable)
|
Ok(colorable)
|
||||||
|
|
@ -3310,6 +3371,10 @@ impl ConfigProxyHandler {
|
||||||
.handle_set_bar_position(position)
|
.handle_set_bar_position(position)
|
||||||
.wrn("set_bar_position")?,
|
.wrn("set_bar_position")?,
|
||||||
ClientMessage::GetBarPosition => self.handle_get_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
|
ClientMessage::SeatFocusHistory { seat, timeline } => self
|
||||||
.handle_seat_focus_history(seat, timeline)
|
.handle_seat_focus_history(seat, timeline)
|
||||||
.wrn("seat_focus_history")?,
|
.wrn("seat_focus_history")?,
|
||||||
|
|
@ -3432,6 +3497,40 @@ impl ConfigProxyHandler {
|
||||||
} => self
|
} => self
|
||||||
.handle_window_resize(window, dx1, dy1, dx2, dy2)
|
.handle_window_resize(window, dx1, dy1, dx2, dy2)
|
||||||
.wrn("window_resize")?,
|
.wrn("window_resize")?,
|
||||||
|
ClientMessage::SeatToggleTab { seat } => self
|
||||||
|
.handle_seat_toggle_tab(seat)
|
||||||
|
.wrn("seat_toggle_tab")?,
|
||||||
|
ClientMessage::SeatMakeGroup {
|
||||||
|
seat,
|
||||||
|
axis,
|
||||||
|
ephemeral,
|
||||||
|
} => self
|
||||||
|
.handle_seat_make_group(seat, axis, ephemeral)
|
||||||
|
.wrn("seat_make_group")?,
|
||||||
|
ClientMessage::SeatChangeGroupOpposite { seat } => self
|
||||||
|
.handle_seat_change_group_opposite(seat)
|
||||||
|
.wrn("seat_change_group_opposite")?,
|
||||||
|
ClientMessage::SeatEqualize { seat, recursive } => self
|
||||||
|
.handle_seat_equalize(seat, recursive)
|
||||||
|
.wrn("seat_equalize")?,
|
||||||
|
ClientMessage::SetAutotile { enabled } => {
|
||||||
|
self.state.theme.autotile_enabled.set(enabled);
|
||||||
|
}
|
||||||
|
ClientMessage::SeatToggleExpand { .. } => {
|
||||||
|
// Removed feature; kept for binary protocol compatibility.
|
||||||
|
}
|
||||||
|
ClientMessage::SetTabTitleAlign { align } => {
|
||||||
|
use crate::theme::TabTitleAlign;
|
||||||
|
let val = match align {
|
||||||
|
1 => TabTitleAlign::Center,
|
||||||
|
2 => TabTitleAlign::End,
|
||||||
|
_ => TabTitleAlign::Start,
|
||||||
|
};
|
||||||
|
self.state.theme.tab_title_align.set(val);
|
||||||
|
}
|
||||||
|
ClientMessage::SeatMoveTab { seat, right } => self
|
||||||
|
.handle_seat_move_tab(seat, right)
|
||||||
|
.wrn("seat_move_tab")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,8 @@ pub enum GfxApiOpt {
|
||||||
Sync,
|
Sync,
|
||||||
FillRect(FillRect),
|
FillRect(FillRect),
|
||||||
CopyTexture(CopyTexture),
|
CopyTexture(CopyTexture),
|
||||||
|
RoundedFillRect(RoundedFillRect),
|
||||||
|
RoundedCopyTexture(RoundedCopyTexture),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GfxRenderPass {
|
pub struct GfxRenderPass {
|
||||||
|
|
@ -289,6 +291,55 @@ pub struct CopyTexture {
|
||||||
pub alpha_mode: AlphaMode,
|
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,
|
||||||
|
/// Sort order hint within the RoundedFill bucket (lower renders first).
|
||||||
|
pub z_order: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct SyncFile(pub Rc<OwnedFd>);
|
pub struct SyncFile(pub Rc<OwnedFd>);
|
||||||
|
|
||||||
|
|
@ -705,8 +756,8 @@ impl dyn GfxFramebuffer {
|
||||||
let (width, height) = self.logical_size(transform);
|
let (width, height) = self.logical_size(transform);
|
||||||
Rect::new_saturating(0, 0, width, height)
|
Rect::new_saturating(0, 0, width, height)
|
||||||
},
|
},
|
||||||
icons: None,
|
|
||||||
stretch: None,
|
stretch: None,
|
||||||
|
corner_radius: None,
|
||||||
};
|
};
|
||||||
cursor.render_hardware_cursor(&mut renderer);
|
cursor.render_hardware_cursor(&mut renderer);
|
||||||
self.render(
|
self.render(
|
||||||
|
|
@ -1039,8 +1090,8 @@ pub fn create_render_pass(
|
||||||
let (width, height) = logical_size(physical_size, transform);
|
let (width, height) = logical_size(physical_size, transform);
|
||||||
Rect::new_saturating(0, 0, width, height)
|
Rect::new_saturating(0, 0, width, height)
|
||||||
},
|
},
|
||||||
icons: state.icons.get(state, scale),
|
|
||||||
stretch: None,
|
stretch: None,
|
||||||
|
corner_radius: None,
|
||||||
};
|
};
|
||||||
node.node_render(&mut renderer, 0, 0, None);
|
node.node_render(&mut renderer, 0, 0, None);
|
||||||
if let Some(rect) = cursor_rect {
|
if let Some(rect) = cursor_rect {
|
||||||
|
|
@ -1256,6 +1307,8 @@ impl GfxRenderPass {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
GfxApiOpt::CopyTexture(ct) => break 'ct2 ct,
|
GfxApiOpt::CopyTexture(ct) => break 'ct2 ct,
|
||||||
|
GfxApiOpt::RoundedFillRect(_) => return None,
|
||||||
|
GfxApiOpt::RoundedCopyTexture(_) => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -1299,6 +1352,8 @@ impl GfxRenderPass {
|
||||||
// Texture could be visible.
|
// Texture could be visible.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
GfxApiOpt::RoundedFillRect(_) => return None,
|
||||||
|
GfxApiOpt::RoundedCopyTexture(_) => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(clear) = self.clear
|
if let Some(clear) = self.clear
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use {
|
||||||
cmm::cmm_eotf::Eotf,
|
cmm::cmm_eotf::Eotf,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, CopyTexture, FdSync, FramebufferRect, GfxApiOpt, GfxContext, GfxError,
|
AcquireSync, CopyTexture, FdSync, FramebufferRect, GfxApiOpt, GfxContext, GfxError,
|
||||||
GfxTexture, ReleaseSync, SyncFile,
|
GfxTexture, ReleaseSync, RoundedCopyTexture, RoundedFillRect, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
egl::image::EglImage,
|
egl::image::EglImage,
|
||||||
|
|
@ -217,6 +217,14 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
|
||||||
copy_tex.push(c);
|
copy_tex.push(c);
|
||||||
i += 1;
|
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() {
|
if fill_rect.is_not_empty() {
|
||||||
|
|
@ -269,6 +277,12 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
|
||||||
{
|
{
|
||||||
resv.set_sync(user, &file);
|
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);
|
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) {
|
fn handle_explicit_sync(ctx: &GlRenderContext, img: Option<&Rc<EglImage>>, sync: &AcquireSync) {
|
||||||
let Some(sync_file) = sync.get_sync_file() else {
|
let Some(sync_file) = sync.get_sync_file() else {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ dynload! {
|
||||||
glGetAttribLocation: unsafe fn(prog: GLuint, name: *const GLchar) -> GLint,
|
glGetAttribLocation: unsafe fn(prog: GLuint, name: *const GLchar) -> GLint,
|
||||||
glUniform1i: unsafe fn(location: GLint, v0: GLint),
|
glUniform1i: unsafe fn(location: GLint, v0: GLint),
|
||||||
glUniform1f: unsafe fn(location: GLint, v0: GLfloat),
|
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),
|
glUniform4f: unsafe fn(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat),
|
||||||
glVertexAttribPointer: unsafe fn(
|
glVertexAttribPointer: unsafe fn(
|
||||||
index: GLuint,
|
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)]
|
#[derive(Copy, Clone, PartialEq, Linearize)]
|
||||||
pub(in crate::gfx_apis::gl) enum TexCopyType {
|
pub(in crate::gfx_apis::gl) enum TexCopyType {
|
||||||
Identity,
|
Identity,
|
||||||
|
|
@ -86,6 +109,12 @@ pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
||||||
pub(crate) fill_prog_pos: GLint,
|
pub(crate) fill_prog_pos: GLint,
|
||||||
pub(crate) fill_prog_color: 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) gl_state: RefCell<GfxGlState>,
|
||||||
|
|
||||||
pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser,
|
pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser,
|
||||||
|
|
@ -163,6 +192,74 @@ impl GlRenderContext {
|
||||||
include_str!("../shaders/fill.frag.glsl"),
|
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 {
|
Ok(Self {
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
gbm: ctx.dpy.gbm.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_color: unsafe { fill_prog.get_uniform_location(c"color") },
|
||||||
fill_prog,
|
fill_prog,
|
||||||
|
|
||||||
|
rounded_fill_prog,
|
||||||
|
rounded_tex_internal,
|
||||||
|
rounded_tex_external,
|
||||||
|
|
||||||
gl_state: Default::default(),
|
gl_state: Default::default(),
|
||||||
|
|
||||||
buffer_resv_user: 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,
|
semaphore::VulkanSemaphore,
|
||||||
shaders::{
|
shaders::{
|
||||||
ColorManagementData, EotfArgs, FILL_FRAG, FILL_VERT, FillPushConstants,
|
ColorManagementData, EotfArgs, FILL_FRAG, FILL_VERT, FillPushConstants,
|
||||||
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_TEX_FRAG, LEGACY_TEX_VERT,
|
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_ROUNDED_FILL_FRAG,
|
||||||
LegacyFillPushConstants, LegacyTexPushConstants, OUT_FRAG, OUT_VERT,
|
LEGACY_ROUNDED_FILL_VERT, LEGACY_ROUNDED_TEX_FRAG, LEGACY_ROUNDED_TEX_VERT,
|
||||||
OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex, VulkanShader,
|
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,
|
io_uring::IoUring,
|
||||||
|
|
@ -104,6 +109,13 @@ pub struct VulkanRenderer {
|
||||||
pub(super) tex_frag_shader: Rc<VulkanShader>,
|
pub(super) tex_frag_shader: Rc<VulkanShader>,
|
||||||
pub(super) out_vert_shader: Option<Rc<VulkanShader>>,
|
pub(super) out_vert_shader: Option<Rc<VulkanShader>>,
|
||||||
pub(super) out_frag_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) tex_descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
|
||||||
pub(super) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
pub(super) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||||
pub(super) defunct: Cell<bool>,
|
pub(super) defunct: Cell<bool>,
|
||||||
|
|
@ -202,6 +214,8 @@ type Point = [[f32; 2]; 4];
|
||||||
enum VulkanOp {
|
enum VulkanOp {
|
||||||
Fill(VulkanFillOp),
|
Fill(VulkanFillOp),
|
||||||
Tex(VulkanTexOp),
|
Tex(VulkanTexOp),
|
||||||
|
RoundedFill(VulkanRoundedFillOp),
|
||||||
|
RoundedTex(VulkanRoundedTexOp),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VulkanTexOp {
|
struct VulkanTexOp {
|
||||||
|
|
@ -231,6 +245,40 @@ struct VulkanFillOp {
|
||||||
instances: u32,
|
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,
|
||||||
|
z_order: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)]
|
||||||
pub(super) enum RenderPass {
|
pub(super) enum RenderPass {
|
||||||
BlendBuffer,
|
BlendBuffer,
|
||||||
|
|
@ -295,12 +343,20 @@ impl VulkanDevice {
|
||||||
let tex_frag_shader;
|
let tex_frag_shader;
|
||||||
let out_vert_shader;
|
let out_vert_shader;
|
||||||
let out_frag_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();
|
let mut tex_descriptor_set_layouts = ArrayVec::new();
|
||||||
if self.descriptor_buffer.is_some() {
|
if self.descriptor_buffer.is_some() {
|
||||||
tex_vert_shader = self.create_shader(TEX_VERT)?;
|
tex_vert_shader = self.create_shader(TEX_VERT)?;
|
||||||
tex_frag_shader = self.create_shader(TEX_FRAG)?;
|
tex_frag_shader = self.create_shader(TEX_FRAG)?;
|
||||||
fill_vert_shader = self.create_shader(FILL_VERT)?;
|
fill_vert_shader = self.create_shader(FILL_VERT)?;
|
||||||
fill_frag_shader = self.create_shader(FILL_FRAG)?;
|
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_vert_shader = Some(self.create_shader(OUT_VERT)?);
|
||||||
out_frag_shader = Some(self.create_shader(OUT_FRAG)?);
|
out_frag_shader = Some(self.create_shader(OUT_FRAG)?);
|
||||||
tex_descriptor_set_layouts
|
tex_descriptor_set_layouts
|
||||||
|
|
@ -311,6 +367,10 @@ impl VulkanDevice {
|
||||||
tex_frag_shader = self.create_shader(LEGACY_TEX_FRAG)?;
|
tex_frag_shader = self.create_shader(LEGACY_TEX_FRAG)?;
|
||||||
fill_vert_shader = self.create_shader(LEGACY_FILL_VERT)?;
|
fill_vert_shader = self.create_shader(LEGACY_FILL_VERT)?;
|
||||||
fill_frag_shader = self.create_shader(LEGACY_FILL_FRAG)?;
|
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_vert_shader = None;
|
||||||
out_frag_shader = None;
|
out_frag_shader = None;
|
||||||
tex_descriptor_set_layouts
|
tex_descriptor_set_layouts
|
||||||
|
|
@ -400,6 +460,12 @@ impl VulkanDevice {
|
||||||
tex_frag_shader,
|
tex_frag_shader,
|
||||||
out_vert_shader,
|
out_vert_shader,
|
||||||
out_frag_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,
|
tex_descriptor_set_layouts,
|
||||||
out_descriptor_set_layout,
|
out_descriptor_set_layout,
|
||||||
defunct: Cell::new(false),
|
defunct: Cell::new(false),
|
||||||
|
|
@ -457,6 +523,112 @@ impl VulkanRenderer {
|
||||||
Ok(fill_pipelines)
|
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(
|
fn get_or_create_tex_pipelines(
|
||||||
&self,
|
&self,
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
|
|
@ -665,23 +837,34 @@ impl VulkanRenderer {
|
||||||
RenderPass::FrameBuffer => fb_inv_eotf_args_descriptor,
|
RenderPass::FrameBuffer => fb_inv_eotf_args_descriptor,
|
||||||
};
|
};
|
||||||
for cmd in &mut memory.ops[pass] {
|
for cmd in &mut memory.ops[pass] {
|
||||||
let VulkanOp::Tex(c) = cmd else {
|
let (tex, resource_offset, cm_addr, eotf_addr) = match cmd {
|
||||||
continue;
|
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;
|
*resource_offset = resource_writer.next_offset();
|
||||||
c.resource_descriptor_buffer_offset = resource_writer.next_offset();
|
|
||||||
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
||||||
writer.write(
|
writer.write(
|
||||||
tex_descriptor_set_layout.offsets[0],
|
tex_descriptor_set_layout.offsets[0],
|
||||||
tex.sampled_image_descriptor.as_ref().unwrap(),
|
tex.sampled_image_descriptor.as_ref().unwrap(),
|
||||||
);
|
);
|
||||||
if let Some(addr) = c.color_management_data_address {
|
if let Some(addr) = *cm_addr {
|
||||||
writer.write(
|
writer.write(
|
||||||
tex_descriptor_set_layout.offsets[1],
|
tex_descriptor_set_layout.offsets[1],
|
||||||
get_ub_descriptor!(addr, ColorManagementData),
|
get_ub_descriptor!(addr, ColorManagementData),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(addr) = c.eotf_args_address {
|
if let Some(addr) = *eotf_addr {
|
||||||
writer.write(
|
writer.write(
|
||||||
tex_descriptor_set_layout.offsets[2],
|
tex_descriptor_set_layout.offsets[2],
|
||||||
get_ub_descriptor!(addr, EotfArgs),
|
get_ub_descriptor!(addr, EotfArgs),
|
||||||
|
|
@ -741,12 +924,19 @@ impl VulkanRenderer {
|
||||||
enum Key {
|
enum Key {
|
||||||
Fill { color: [u32; 4] },
|
Fill { color: [u32; 4] },
|
||||||
Tex(usize),
|
Tex(usize),
|
||||||
|
RoundedFill { z_order: u32, color: [u32; 4] },
|
||||||
|
RoundedTex(usize),
|
||||||
}
|
}
|
||||||
match o {
|
match o {
|
||||||
VulkanOp::Fill(f) => Key::Fill {
|
VulkanOp::Fill(f) => Key::Fill {
|
||||||
color: f.color.map(|c| c.to_bits()),
|
color: f.color.map(|c| c.to_bits()),
|
||||||
},
|
},
|
||||||
VulkanOp::Tex(t) => Key::Tex(t.index),
|
VulkanOp::Tex(t) => Key::Tex(t.index),
|
||||||
|
VulkanOp::RoundedFill(f) => Key::RoundedFill {
|
||||||
|
z_order: f.z_order,
|
||||||
|
color: f.color.map(|c| c.to_bits()),
|
||||||
|
},
|
||||||
|
VulkanOp::RoundedTex(t) => Key::RoundedTex(t.index),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mops = &mut memory.ops[pass];
|
let mops = &mut memory.ops[pass];
|
||||||
|
|
@ -782,6 +972,22 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
mops.push(VulkanOp::Tex(c));
|
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 +1123,106 @@ 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,
|
||||||
|
z_order: rf.z_order,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
sync(memory);
|
||||||
|
|
@ -998,6 +1304,12 @@ impl VulkanRenderer {
|
||||||
VulkanOp::Tex(c) => {
|
VulkanOp::Tex(c) => {
|
||||||
c.range_address += buffer.buffer.address;
|
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,9 +1341,16 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
for ops in memory.ops.values_mut() {
|
for ops in memory.ops.values_mut() {
|
||||||
for op in ops {
|
for op in ops {
|
||||||
if let VulkanOp::Tex(c) = op {
|
match op {
|
||||||
adj!(&mut c.color_management_data_address);
|
VulkanOp::Tex(c) => {
|
||||||
adj!(&mut c.eotf_args_address);
|
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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1051,8 +1370,12 @@ impl VulkanRenderer {
|
||||||
let execution = self.allocate_point();
|
let execution = self.allocate_point();
|
||||||
for pass in RenderPass::variants() {
|
for pass in RenderPass::variants() {
|
||||||
for cmd in &mut memory.ops[pass] {
|
for cmd in &mut memory.ops[pass] {
|
||||||
if let VulkanOp::Tex(c) = cmd {
|
let tex_data = match cmd {
|
||||||
let tex = &c.tex;
|
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 {
|
if tex.execution_version.replace(execution) == execution {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1066,9 +1389,9 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
memory.textures.push(UsedTexture {
|
memory.textures.push(UsedTexture {
|
||||||
tex: tex.clone(),
|
tex: tex.clone(),
|
||||||
resv: c.buffer_resv.take(),
|
resv: buffer_resv.take(),
|
||||||
acquire_sync: c.acquire_sync.take().unwrap(),
|
acquire_sync: acquire_sync.take().unwrap(),
|
||||||
release_sync: c.release_sync,
|
release_sync,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1301,6 +1624,10 @@ impl VulkanRenderer {
|
||||||
let memory = &*self.memory.borrow();
|
let memory = &*self.memory.borrow();
|
||||||
let fill_pl = self.get_or_create_fill_pipelines(target.format.vk_format)?;
|
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 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 dev = &self.device.device;
|
||||||
let mut current_pipeline = None;
|
let mut current_pipeline = None;
|
||||||
let mut bind = |pipeline: &VulkanPipeline| {
|
let mut bind = |pipeline: &VulkanPipeline| {
|
||||||
|
|
@ -1421,6 +1748,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(())
|
Ok(())
|
||||||
|
|
@ -1933,6 +2388,14 @@ impl VulkanRenderer {
|
||||||
};
|
};
|
||||||
(opaque, c.target)
|
(opaque, c.target)
|
||||||
}
|
}
|
||||||
|
GfxApiOpt::RoundedFillRect(rf) => {
|
||||||
|
// Rounded rects are never fully opaque due to AA at corners,
|
||||||
|
// but they do paint pixels and need paint regions.
|
||||||
|
(false, rf.rect)
|
||||||
|
}
|
||||||
|
GfxApiOpt::RoundedCopyTexture(ct) => {
|
||||||
|
(false, ct.target)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if opaque || bb.is_none() {
|
if opaque || bb.is_none() {
|
||||||
tag |= 1;
|
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_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_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 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 struct VulkanShader {
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
|
|
@ -99,6 +111,69 @@ pub struct LegacyTexPushConstants {
|
||||||
|
|
||||||
unsafe impl Packed for 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)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct OutPushConstants {
|
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
|
c315a064b48dd5bdb607a6b79c30d31b6e59ffec69e93d50ab875abf97c41bbf src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl
|
||||||
590d061b97446fc501158609eaf098b71bc7b328c008b586ff36613ce690d618 src/gfx_apis/vulkan/shaders/legacy/fill.frag
|
590d061b97446fc501158609eaf098b71bc7b328c008b586ff36613ce690d618 src/gfx_apis/vulkan/shaders/legacy/fill.frag
|
||||||
ad22a79e1a88a12daa40c0a2b953084c129a408297c8ca544d60e0b6001470b9 src/gfx_apis/vulkan/shaders/legacy/fill.vert
|
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
|
e0a8769dd7938dd02e66db9e9048ed6bef8f8c42671f2e2c7a7976a6d498f685 src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl
|
||||||
0e7c72ea11671065842c8b4ad4131a7df33b427dc0ea76bf5a896546f6636cb0 src/gfx_apis/vulkan/shaders/legacy/tex.frag
|
0e7c72ea11671065842c8b4ad4131a7df33b427dc0ea76bf5a896546f6636cb0 src/gfx_apis/vulkan/shaders/legacy/tex.frag
|
||||||
4402f7ccdbb9fb52fb6cda3aab13cf89e2980c79b541f8be0463efd64a5f98ed src/gfx_apis/vulkan/shaders/legacy/tex.vert
|
4402f7ccdbb9fb52fb6cda3aab13cf89e2980c79b541f8be0463efd64a5f98ed src/gfx_apis/vulkan/shaders/legacy/tex.vert
|
||||||
3ba5d05c2b95099e5424b3ade5d1c31d431f5730b1d0b51a9fb5f8afc4ea14b4 src/gfx_apis/vulkan/shaders/out.common.glsl
|
3ba5d05c2b95099e5424b3ade5d1c31d431f5730b1d0b51a9fb5f8afc4ea14b4 src/gfx_apis/vulkan/shaders/out.common.glsl
|
||||||
5069f619c7d722815a022e2d84720a2d8290af49a3ed49ea0cd26b52115cc39a src/gfx_apis/vulkan/shaders/out.frag
|
5069f619c7d722815a022e2d84720a2d8290af49a3ed49ea0cd26b52115cc39a src/gfx_apis/vulkan/shaders/out.frag
|
||||||
0adc7e12328c15fb3e7e6c8b8701a182223c2f15337e14131f41dd247e697809 src/gfx_apis/vulkan/shaders/out.vert
|
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
|
e22d4d3318a350def8ef19c7b27dc6a308a84c2fe9d7c02b81107f72073cd481 src/gfx_apis/vulkan/shaders/tex.common.glsl
|
||||||
1f196cee646a934072beb3e5648a5042c035953d9a0c26b0a22e330c2f8bb994 src/gfx_apis/vulkan/shaders/tex.frag
|
1f196cee646a934072beb3e5648a5042c035953d9a0c26b0a22e330c2f8bb994 src/gfx_apis/vulkan/shaders/tex.frag
|
||||||
423cf327c9fcc4070dbf75321c1224a1589b6cf3d2f1ea5e8bd0362e1a9f3aa1 src/gfx_apis/vulkan/shaders/tex.vert
|
423cf327c9fcc4070dbf75321c1224a1589b6cf3d2f1ea5e8bd0362e1a9f3aa1 src/gfx_apis/vulkan/shaders/tex.vert
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#![allow(clippy::excessive_precision)]
|
#![allow(clippy::excessive_precision)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
|
@ -22,12 +23,14 @@ pub struct Icons {
|
||||||
icons: CopyHashMap<i32, Option<Rc<SizedIcons>>>,
|
icons: CopyHashMap<i32, Option<Rc<SizedIcons>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug, Linearize)]
|
#[derive(Copy, Clone, Debug, Linearize)]
|
||||||
pub enum IconState {
|
pub enum IconState {
|
||||||
Active,
|
Active,
|
||||||
Passive,
|
Passive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct SizedIcons {
|
pub struct SizedIcons {
|
||||||
pub pin_unfocused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
pub pin_unfocused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
||||||
pub pin_focused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
pub pin_focused_title: StaticMap<IconState, Rc<dyn GfxTexture>>,
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::{DeviceHandlerData, State},
|
state::{DeviceHandlerData, State},
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, ContainerSplit, Direction, FoundNode, Node, NodeId, NodeLayer,
|
ContainerNode, ContainerSplit, ChangeGroupAction, Direction, FoundNode, Node, NodeId, NodeLayer,
|
||||||
NodeLayerLink, NodeLocation, OutputNode, StackedNode, ToplevelNode, WorkspaceNode,
|
NodeLayerLink, NodeLocation, OutputNode, StackedNode, ToplevelNode, WorkspaceNode,
|
||||||
generic_node_visitor, toplevel_create_split, toplevel_parent_container,
|
generic_node_visitor, toplevel_create_split, toplevel_parent_container,
|
||||||
toplevel_set_floating, toplevel_set_workspace,
|
toplevel_set_floating, toplevel_set_workspace,
|
||||||
|
|
@ -745,6 +745,40 @@ impl WlSeatGlobal {
|
||||||
toplevel_create_split(&self.state, tl, axis);
|
toplevel_create_split(&self.state, tl, axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_tab(&self) {
|
||||||
|
if let Some(c) = self.kb_parent_container() {
|
||||||
|
c.change_group(ChangeGroupAction::ToggleTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_group(&self, axis: ContainerSplit, ephemeral: bool) {
|
||||||
|
if let Some(c) = self.kb_parent_container() {
|
||||||
|
c.make_group(axis, ephemeral);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_group_opposite(&self) {
|
||||||
|
if let Some(c) = self.kb_parent_container() {
|
||||||
|
c.change_group(ChangeGroupAction::Opposite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn equalize(&self, recursive: bool) {
|
||||||
|
if let Some(c) = self.kb_parent_container() {
|
||||||
|
if recursive {
|
||||||
|
c.equalize_recursive();
|
||||||
|
} else {
|
||||||
|
c.equalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_tab(&self, right: bool) {
|
||||||
|
if let Some(c) = self.kb_parent_container() {
|
||||||
|
c.move_tab(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_parent(self: &Rc<Self>) {
|
pub fn focus_parent(self: &Rc<Self>) {
|
||||||
if let Some(tl) = self.keyboard_node.get().node_toplevel()
|
if let Some(tl) = self.keyboard_node.get().node_toplevel()
|
||||||
&& let Some(parent) = tl.tl_data().parent.get()
|
&& let Some(parent) = tl.tl_data().parent.get()
|
||||||
|
|
@ -1320,6 +1354,7 @@ impl WlSeatGlobal {
|
||||||
.start_drag(self, origin, source, icon, serial)
|
.start_drag(self, origin, source, icon, serial)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn start_tile_drag(self: &Rc<Self>, tl: &Rc<dyn ToplevelNode>) {
|
pub fn start_tile_drag(self: &Rc<Self>, tl: &Rc<dyn ToplevelNode>) {
|
||||||
if self.state.ui_drag_enabled.get() {
|
if self.state.ui_drag_enabled.get() {
|
||||||
self.pointer_owner.start_tile_drag(self, tl);
|
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>) {
|
pub fn start_tile_drag(&self, seat: &Rc<WlSeatGlobal>, tl: &Rc<dyn ToplevelNode>) {
|
||||||
self.owner.get().start_tile_drag(seat, tl);
|
self.owner.get().start_tile_drag(seat, tl);
|
||||||
}
|
}
|
||||||
|
|
@ -288,6 +289,7 @@ trait PointerOwner {
|
||||||
fn disable_window_management(&self, seat: &Rc<WlSeatGlobal>) {
|
fn disable_window_management(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
let _ = seat;
|
let _ = seat;
|
||||||
}
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
fn start_tile_drag(&self, seat: &Rc<WlSeatGlobal>, tl: &Rc<dyn ToplevelNode>) {
|
fn start_tile_drag(&self, seat: &Rc<WlSeatGlobal>, tl: &Rc<dyn ToplevelNode>) {
|
||||||
let _ = seat;
|
let _ = seat;
|
||||||
let _ = tl;
|
let _ = tl;
|
||||||
|
|
@ -732,6 +734,7 @@ trait SimplePointerOwnerUsecase: Sized + Clone + 'static {
|
||||||
let _ = seat;
|
let _ = seat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn start_tile_drag(
|
fn start_tile_drag(
|
||||||
&self,
|
&self,
|
||||||
grab: &SimpleGrabPointerOwner<Self>,
|
grab: &SimpleGrabPointerOwner<Self>,
|
||||||
|
|
|
||||||
|
|
@ -528,6 +528,8 @@ impl GfxFramebuffer for TestGfxFb {
|
||||||
GfxApiOpt::Sync => {}
|
GfxApiOpt::Sync => {}
|
||||||
GfxApiOpt::FillRect(f) => fill_rect(&f, staging),
|
GfxApiOpt::FillRect(f) => fill_rect(&f, staging),
|
||||||
GfxApiOpt::CopyTexture(c) => copy_texture(&c, staging)?,
|
GfxApiOpt::CopyTexture(c) => copy_texture(&c, staging)?,
|
||||||
|
GfxApiOpt::RoundedFillRect(_) => {}
|
||||||
|
GfxApiOpt::RoundedCopyTexture(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy_from_staging(staging);
|
copy_from_staging(staging);
|
||||||
|
|
|
||||||
483
src/renderer.rs
483
src/renderer.rs
|
|
@ -2,7 +2,6 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
cmm::cmm_render_intent::RenderIntent,
|
cmm::cmm_render_intent::RenderIntent,
|
||||||
gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
|
gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
|
||||||
icons::{IconState, SizedIcons},
|
|
||||||
ifs::wl_surface::{
|
ifs::wl_surface::{
|
||||||
SurfaceBuffer, WlSurface,
|
SurfaceBuffer, WlSurface,
|
||||||
x_surface::xwindow::Xwindow,
|
x_surface::xwindow::Xwindow,
|
||||||
|
|
@ -13,10 +12,10 @@ use {
|
||||||
renderer::renderer_base::RendererBase,
|
renderer::renderer_base::RendererBase,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
theme::Color,
|
theme::{Color, CornerRadius},
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||||
ToplevelNodeBase, WorkspaceNode,
|
ToplevelNodeBase, WorkspaceNode, tab_bar::TabBar,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{ops::Deref, rc::Rc, slice},
|
std::{ops::Deref, rc::Rc, slice},
|
||||||
|
|
@ -29,8 +28,8 @@ pub struct Renderer<'a> {
|
||||||
pub state: &'a State,
|
pub state: &'a State,
|
||||||
pub logical_extents: Rect,
|
pub logical_extents: Rect,
|
||||||
pub pixel_extents: Rect,
|
pub pixel_extents: Rect,
|
||||||
pub icons: Option<Rc<SizedIcons>>,
|
|
||||||
pub stretch: Option<(i32, i32)>,
|
pub stretch: Option<(i32, i32)>,
|
||||||
|
pub corner_radius: Option<CornerRadius>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer<'_> {
|
impl Renderer<'_> {
|
||||||
|
|
@ -278,68 +277,126 @@ impl Renderer<'_> {
|
||||||
self.render_tl_aux(placeholder.tl_data(), bounds, true);
|
self.render_tl_aux(placeholder.tl_data(), bounds, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_container_decorations(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
fn render_tab_bar(&mut self, tab_bar: &TabBar, x: i32, y: i32, _container_width: i32) {
|
||||||
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
||||||
let srgb = &srgb_srgb.linear;
|
let srgb = &srgb_srgb.linear;
|
||||||
let perceptual = RenderIntent::Perceptual;
|
let perceptual = RenderIntent::Perceptual;
|
||||||
let rd = container.render_data.borrow_mut();
|
let scalef = self.base.scalef as f32;
|
||||||
let c = self.state.theme.colors.unfocused_title_background.get();
|
|
||||||
self.base
|
let radius = self.state.theme.sizes.tab_bar_radius.get();
|
||||||
.fill_boxes2(&rd.title_rects, &c, srgb, perceptual, x, y);
|
let border_width = self.state.theme.sizes.tab_bar_border_width.get();
|
||||||
let c = self.state.theme.colors.focused_title_background.get();
|
let text_padding = self.state.theme.sizes.tab_bar_text_padding.get();
|
||||||
self.base
|
let bar_height = tab_bar.height;
|
||||||
.fill_boxes2(&rd.active_title_rects, &c, srgb, perceptual, x, y);
|
let render_scale = tab_bar.render_scale;
|
||||||
let c = self.state.theme.colors.attention_requested_background.get();
|
|
||||||
self.base
|
// Vulkan sorts ops: Fill < Tex < RoundedFill (by z_order, color) < RoundedTex.
|
||||||
.fill_boxes2(&rd.attention_title_rects, &c, srgb, perceptual, x, y);
|
// We use:
|
||||||
let c = self.state.theme.colors.separator.get();
|
// FillRect – tiny strip for Vulkan paint regions (hidden)
|
||||||
self.base
|
// RoundedFillRect z0 – solid rounded bg
|
||||||
.fill_boxes2(&rd.underline_rects, &c, srgb, perceptual, x, y);
|
// RoundedFillRect z1 – rounded border ring (on top of bg)
|
||||||
let c = self.state.theme.colors.border.get();
|
// RoundedCopyTexture – title text (on top of everything)
|
||||||
self.base
|
for entry in &tab_bar.entries {
|
||||||
.fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y);
|
let (bg_color, border_color, _text_color) =
|
||||||
if let Some(lar) = &rd.last_active_rect {
|
TabBar::entry_colors(self.state, entry);
|
||||||
let c = self
|
let ex = entry.x.get();
|
||||||
.state
|
let ew = entry.width.get();
|
||||||
.theme
|
let tab_rect = Rect::new_sized_saturating(ex, 0, ew, bar_height);
|
||||||
.colors
|
let tab_cr = CornerRadius::from(radius as f32);
|
||||||
.focused_inactive_title_background
|
|
||||||
.get();
|
// Tiny FillRect strip to establish Vulkan paint regions (visually hidden
|
||||||
|
// behind the RoundedFillRect bg that renders later).
|
||||||
|
let strip = Rect::new_sized_saturating(ex + radius, bar_height / 2, (ew - 2 * radius).max(1), 1);
|
||||||
self.base
|
self.base
|
||||||
.fill_boxes2(std::slice::from_ref(lar), &c, srgb, perceptual, x, y);
|
.fill_boxes2(slice::from_ref(&strip), &bg_color, srgb, perceptual, x, y);
|
||||||
}
|
|
||||||
if let Some(titles) = rd.titles.get(&self.base.scale) {
|
// Rounded solid bg fill (z_order=0, renders first among RoundedFill).
|
||||||
for title in titles {
|
self.base.fill_rounded_rect_z(
|
||||||
let rect = title.rect.move_(x, y);
|
tab_rect.move_(x, y),
|
||||||
let bounds = self.base.scale_rect(rect);
|
&bg_color,
|
||||||
let (x, y) = self.base.scale_point(rect.x1(), rect.y1());
|
None,
|
||||||
self.base.render_texture(
|
srgb,
|
||||||
&title.tex,
|
perceptual,
|
||||||
|
tab_cr.scaled_by(scalef),
|
||||||
|
0.0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rounded border ring on top (z_order=1, renders after bg).
|
||||||
|
if border_width > 0 {
|
||||||
|
self.base.fill_rounded_rect_z(
|
||||||
|
tab_rect.move_(x, y),
|
||||||
|
&border_color,
|
||||||
None,
|
None,
|
||||||
x,
|
srgb,
|
||||||
y,
|
perceptual,
|
||||||
|
tab_cr.scaled_by(scalef),
|
||||||
|
border_width as f32 * scalef,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title text as RoundedCopyTexture (sorts after all RoundedFill).
|
||||||
|
let tex_ref = entry.title_texture.borrow();
|
||||||
|
if let Some(tex) = tex_ref.as_ref()
|
||||||
|
&& let Some(texture) = tex.texture()
|
||||||
|
{
|
||||||
|
use crate::theme::TabTitleAlign;
|
||||||
|
let (tw, _th) = texture.size();
|
||||||
|
let tex_width = (tw as f64 / render_scale.to_f64()).round() as i32;
|
||||||
|
let tab_inner = ew - 2 * (text_padding + border_width);
|
||||||
|
let text_x = match self.state.theme.tab_title_align.get() {
|
||||||
|
TabTitleAlign::Start => x + ex + text_padding + border_width,
|
||||||
|
TabTitleAlign::Center => {
|
||||||
|
x + ex + border_width + (tab_inner.max(0) - tex_width).max(0) / 2 + text_padding.min(tab_inner.max(0) / 2)
|
||||||
|
}
|
||||||
|
TabTitleAlign::End => {
|
||||||
|
let end_x = x + ex + ew - tex_width - text_padding - border_width;
|
||||||
|
end_x.max(x + ex + border_width)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (tx, ty) = self.base.scale_point(text_x, y);
|
||||||
|
self.base.render_rounded_texture(
|
||||||
|
&texture,
|
||||||
|
None,
|
||||||
|
tx,
|
||||||
|
ty,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
render_scale,
|
||||||
Some(&bounds),
|
None,
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
self.state.color_manager.srgb_gamma22(),
|
||||||
srgb_srgb,
|
|
||||||
perceptual,
|
perceptual,
|
||||||
AlphaMode::PremultipliedElectrical,
|
AlphaMode::PremultipliedElectrical,
|
||||||
|
CornerRadius::from(0.0_f32),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
fn render_container_decorations(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||||
let floating = self.state.theme.floating_titles.get();
|
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
||||||
|
let srgb = &srgb_srgb.linear;
|
||||||
|
let perceptual = RenderIntent::Perceptual;
|
||||||
|
let rd = container.render_data.borrow_mut();
|
||||||
|
let c = self.state.theme.colors.border.get();
|
||||||
|
self.base
|
||||||
|
.fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||||
self.render_container_decorations(container, x, y);
|
self.render_container_decorations(container, x, y);
|
||||||
|
|
||||||
if let Some(child) = container.mono_child.get() {
|
if let Some(child) = container.mono_child.get() {
|
||||||
|
// Render tab bar if present.
|
||||||
|
{
|
||||||
|
let tab_bar = container.tab_bar.borrow();
|
||||||
|
if let Some(tb) = tab_bar.as_ref() {
|
||||||
|
self.render_tab_bar(tb, x, y, container.width.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
let mb = container.mono_body.get();
|
let mb = container.mono_body.get();
|
||||||
if self.state.theme.sizes.gap.get() != 0 {
|
if self.state.theme.sizes.gap.get() != 0 {
|
||||||
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
let srgb_srgb = self.state.color_manager.srgb_gamma22();
|
||||||
|
|
@ -355,44 +412,37 @@ impl Renderer<'_> {
|
||||||
let full_w = mb.width();
|
let full_w = mb.width();
|
||||||
let srgb = &srgb_srgb.linear;
|
let srgb = &srgb_srgb.linear;
|
||||||
let perceptual = RenderIntent::Perceptual;
|
let perceptual = RenderIntent::Perceptual;
|
||||||
if floating {
|
if !child.node.node_is_container() {
|
||||||
if !child.node.node_is_container() {
|
let cr = self.state.theme.corner_radius.get();
|
||||||
let body_frame = [
|
let frame_y = mb.y1();
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, mb.y1(), bw, mb.y2() - mb.y1()),
|
let frame_h = mb.height();
|
||||||
Rect::new_sized_saturating(mb.x2(), mb.y1(), bw, mb.y2() - mb.y1()),
|
if cr.is_zero() {
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, mb.y1() - bw, full_w + 2 * bw, bw),
|
let frame_rects = [
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, mb.y2(), full_w + 2 * bw, bw),
|
Rect::new_sized_saturating(mb.x1() - bw, frame_y, bw, frame_h),
|
||||||
|
Rect::new_sized_saturating(mb.x2(), frame_y, bw, frame_h),
|
||||||
|
Rect::new_sized_saturating(mb.x1() - bw, frame_y - bw, full_w + 2 * bw, bw),
|
||||||
|
Rect::new_sized_saturating(mb.x1() - bw, frame_y + frame_h, full_w + 2 * bw, bw),
|
||||||
];
|
];
|
||||||
self.base.fill_boxes2(&body_frame, c, srgb, perceptual, x, y);
|
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
|
||||||
|
} else {
|
||||||
|
let outer = Rect::new_sized_saturating(
|
||||||
|
mb.x1() - bw,
|
||||||
|
frame_y - bw,
|
||||||
|
full_w + 2 * bw,
|
||||||
|
frame_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 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 full_h = mb.y2();
|
|
||||||
let frame_rects = [
|
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, 0, bw, full_h),
|
|
||||||
Rect::new_sized_saturating(mb.x2(), 0, bw, full_h),
|
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, -bw, full_w + 2 * bw, bw),
|
|
||||||
Rect::new_sized_saturating(mb.x1() - bw, full_h, full_w + 2 * bw, bw),
|
|
||||||
];
|
|
||||||
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let body = mb.move_(x, y);
|
let body = mb.move_(x, y);
|
||||||
|
|
@ -403,28 +453,36 @@ impl Renderer<'_> {
|
||||||
} else {
|
} else {
|
||||||
None
|
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
|
child
|
||||||
.node
|
.node
|
||||||
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
.node_render(self, x + content.x1(), y + content.y1(), Some(&body));
|
||||||
self.stretch = None;
|
self.stretch = None;
|
||||||
} else {
|
} else {
|
||||||
let gap = self.state.theme.sizes.gap.get();
|
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 srgb_srgb = self.state.color_manager.srgb_gamma22();
|
||||||
let bw = self.state.theme.sizes.border_width.get();
|
let bw = self.state.theme.sizes.border_width.get();
|
||||||
let border_color = self.state.theme.colors.border.get();
|
let border_color = self.state.theme.colors.border.get();
|
||||||
let focused_border_color = self.state.theme.colors.focused_title_background.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),
|
Some(srgb_srgb),
|
||||||
bw,
|
bw,
|
||||||
border_color,
|
border_color,
|
||||||
focused_border_color,
|
focused_border_color,
|
||||||
tpuh,
|
|
||||||
)
|
)
|
||||||
} else {
|
} 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() {
|
for child in container.children.iter() {
|
||||||
let body = child.body.get();
|
let body = child.body.get();
|
||||||
if body.x1() >= container.width.get() || body.y1() >= container.height.get() {
|
if body.x1() >= container.width.get() || body.y1() >= container.height.get() {
|
||||||
|
|
@ -432,45 +490,42 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
if let Some(srgb_srgb) = srgb_srgb {
|
if let Some(srgb_srgb) = srgb_srgb {
|
||||||
let srgb = &srgb_srgb.linear;
|
let srgb = &srgb_srgb.linear;
|
||||||
let c = if floating {
|
let c = if child.border_color_is_focused.get() {
|
||||||
if child.active.get() { &focused_border_color } else { &border_color }
|
|
||||||
} else if child.border_color_is_focused.get() {
|
|
||||||
&focused_border_color
|
&focused_border_color
|
||||||
} else {
|
} else {
|
||||||
&border_color
|
&border_color
|
||||||
};
|
};
|
||||||
let title_rect = child.title_rect.get();
|
if !child.node.node_is_container() && gap != 0 {
|
||||||
let full_w = body.width();
|
let full_w = body.width();
|
||||||
let perceptual = RenderIntent::Perceptual;
|
let perceptual = RenderIntent::Perceptual;
|
||||||
if floating && tpuh > 0 {
|
let full_h = body.height();
|
||||||
let tw = title_rect.width();
|
if cr.is_zero() {
|
||||||
let title_h = title_rect.height();
|
let frame_rects = [
|
||||||
let title_frame = [
|
Rect::new_sized_saturating(body.x1() - bw, body.y1(), bw, full_h),
|
||||||
Rect::new_sized_saturating(title_rect.x1() - bw, title_rect.y1() - bw, tw + 2 * bw, bw),
|
Rect::new_sized_saturating(body.x2(), body.y1(), bw, full_h),
|
||||||
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()),
|
|
||||||
Rect::new_sized_saturating(body.x1() - bw, body.y1() - bw, full_w + 2 * bw, bw),
|
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),
|
Rect::new_sized_saturating(body.x1() - bw, body.y2(), full_w + 2 * bw, bw),
|
||||||
];
|
];
|
||||||
self.base.fill_boxes2(&body_frame, c, srgb, perceptual, x, y);
|
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
|
||||||
|
} else {
|
||||||
|
let outer = Rect::new_sized_saturating(
|
||||||
|
body.x1() - bw,
|
||||||
|
body.y1() - bw,
|
||||||
|
full_w + 2 * bw,
|
||||||
|
full_h + 2 * bw,
|
||||||
|
);
|
||||||
|
let scalef = self.base.scalef as f32;
|
||||||
|
let scaled_cr = cr.scaled_by(scalef);
|
||||||
|
self.base.fill_rounded_rect(
|
||||||
|
outer.move_(x, y),
|
||||||
|
c,
|
||||||
|
None,
|
||||||
|
srgb,
|
||||||
|
perceptual,
|
||||||
|
scaled_cr,
|
||||||
|
bw as f32 * scalef,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if !child.node.node_is_container() && gap != 0 {
|
|
||||||
let top_y = if tpuh > 0 { title_rect.y1() } else { body.y1() };
|
|
||||||
let full_h = body.y2() - top_y;
|
|
||||||
let frame_rects = [
|
|
||||||
Rect::new_sized_saturating(body.x1() - bw, top_y, bw, full_h),
|
|
||||||
Rect::new_sized_saturating(body.x2(), top_y, bw, full_h),
|
|
||||||
Rect::new_sized_saturating(body.x1() - bw, top_y - bw, full_w + 2 * bw, bw),
|
|
||||||
Rect::new_sized_saturating(body.x1() - bw, body.y2(), full_w + 2 * bw, bw),
|
|
||||||
];
|
|
||||||
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let content = child.content.get();
|
let content = child.content.get();
|
||||||
|
|
@ -479,6 +534,11 @@ impl Renderer<'_> {
|
||||||
} else {
|
} else {
|
||||||
None
|
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 = body.move_(x, y);
|
||||||
let body = self.base.scale_rect(body);
|
let body = self.base.scale_rect(body);
|
||||||
child
|
child
|
||||||
|
|
@ -642,28 +702,49 @@ impl Renderer<'_> {
|
||||||
let cd = surface.color_description();
|
let cd = surface.color_description();
|
||||||
let intent = surface.render_intent();
|
let intent = surface.render_intent();
|
||||||
let alpha_mode = surface.alpha_mode();
|
let alpha_mode = surface.alpha_mode();
|
||||||
|
let corner_radius = self.corner_radius.take();
|
||||||
if let Some(tex) = buf.get_texture(surface) {
|
if let Some(tex) = buf.get_texture(surface) {
|
||||||
let mut opaque = surface.opaque();
|
if let Some(cr) = corner_radius {
|
||||||
if !opaque && tex.format().has_alpha {
|
self.base.render_rounded_texture(
|
||||||
opaque = self.bounds_are_opaque(x, y, bounds, surface);
|
&tex,
|
||||||
|
alpha,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
Some(tpoints),
|
||||||
|
Some(tsize),
|
||||||
|
self.base.scale,
|
||||||
|
bounds,
|
||||||
|
Some(buffer.clone()),
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
buffer.release_sync,
|
||||||
|
&cd,
|
||||||
|
intent,
|
||||||
|
alpha_mode,
|
||||||
|
cr,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mut opaque = surface.opaque();
|
||||||
|
if !opaque && tex.format().has_alpha {
|
||||||
|
opaque = self.bounds_are_opaque(x, y, bounds, surface);
|
||||||
|
}
|
||||||
|
self.base.render_texture(
|
||||||
|
&tex,
|
||||||
|
alpha,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
Some(tpoints),
|
||||||
|
Some(tsize),
|
||||||
|
self.base.scale,
|
||||||
|
bounds,
|
||||||
|
Some(buffer.clone()),
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
buffer.release_sync,
|
||||||
|
opaque,
|
||||||
|
&cd,
|
||||||
|
intent,
|
||||||
|
alpha_mode,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.base.render_texture(
|
|
||||||
&tex,
|
|
||||||
alpha,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
Some(tpoints),
|
|
||||||
Some(tsize),
|
|
||||||
self.base.scale,
|
|
||||||
bounds,
|
|
||||||
Some(buffer.clone()),
|
|
||||||
AcquireSync::Unnecessary,
|
|
||||||
buffer.release_sync,
|
|
||||||
opaque,
|
|
||||||
&cd,
|
|
||||||
intent,
|
|
||||||
alpha_mode,
|
|
||||||
);
|
|
||||||
} else if let Some(color) = &buf.color {
|
} else if let Some(color) = &buf.color {
|
||||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||||
let rect = match bounds {
|
let rect = match bounds {
|
||||||
|
|
@ -691,128 +772,60 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
let pos = floating.position.get();
|
let pos = floating.position.get();
|
||||||
let theme = &self.state.theme;
|
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 bw = theme.sizes.border_width.get();
|
||||||
let bc = if floating.active.get() {
|
let bc = if floating.active.get() {
|
||||||
theme.colors.focused_title_background.get()
|
theme.colors.focused_title_background.get()
|
||||||
} else {
|
} else {
|
||||||
theme.colors.border.get()
|
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 = self.state.color_manager.srgb_gamma22();
|
||||||
let srgb = &srgb_srgb.linear;
|
let srgb = &srgb_srgb.linear;
|
||||||
let perceptual = RenderIntent::Perceptual;
|
let perceptual = RenderIntent::Perceptual;
|
||||||
if floating_title {
|
let cr = theme.corner_radius.get();
|
||||||
let title_frame = [
|
if cr.is_zero() {
|
||||||
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 borders = [
|
let borders = [
|
||||||
Rect::new_sized_saturating(x, y, pos.width(), bw),
|
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, y + bw, bw, pos.height() - bw),
|
||||||
Rect::new_sized_saturating(x + pos.width() - bw, y + bw, bw, pos.height() - bw),
|
Rect::new_sized_saturating(
|
||||||
Rect::new_sized_saturating(x + bw, y + pos.height() - bw, pos.width() - 2 * bw, bw),
|
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);
|
self.base.fill_boxes(&borders, &bc, srgb, perceptual);
|
||||||
let title = [Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, th)];
|
} else {
|
||||||
self.base.fill_boxes(&title, &tc, srgb, perceptual);
|
let outer = Rect::new_sized_saturating(x, y, pos.width(), pos.height());
|
||||||
let title_underline = [Rect::new_sized_saturating(x + bw, y + bw + th, pos.width() - 2 * bw, tuh)];
|
let scalef = self.base.scalef as f32;
|
||||||
self.base.fill_boxes(&title_underline, &uc, srgb, perceptual);
|
let scaled_cr = cr.scaled_by(scalef);
|
||||||
}
|
self.base.fill_rounded_rect(
|
||||||
let rect = floating.title_rect.get().move_(x, y);
|
outer,
|
||||||
let bounds = self.base.scale_rect(rect);
|
&bc,
|
||||||
let (mut x1, y1) = rect.position();
|
|
||||||
let is_pinned = floating.pinned_link.borrow().is_some();
|
|
||||||
if is_pinned || self.state.show_pin_icon.get() {
|
|
||||||
let (x, y) = self.base.scale_point(x1, y1);
|
|
||||||
if let Some(icons) = &self.icons {
|
|
||||||
let icon = if floating.active.get() {
|
|
||||||
&icons.pin_focused_title
|
|
||||||
} else if floating.attention_requested.get() {
|
|
||||||
&icons.pin_attention_requested
|
|
||||||
} else {
|
|
||||||
&icons.pin_unfocused_title
|
|
||||||
};
|
|
||||||
let state = match is_pinned {
|
|
||||||
true => IconState::Active,
|
|
||||||
false => IconState::Passive,
|
|
||||||
};
|
|
||||||
self.base.render_texture(
|
|
||||||
&icon[state],
|
|
||||||
None,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
self.base.scale,
|
|
||||||
Some(&bounds),
|
|
||||||
None,
|
|
||||||
AcquireSync::None,
|
|
||||||
ReleaseSync::None,
|
|
||||||
false,
|
|
||||||
srgb_srgb,
|
|
||||||
perceptual,
|
|
||||||
AlphaMode::PremultipliedElectrical,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
x1 += th;
|
|
||||||
}
|
|
||||||
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale)
|
|
||||||
&& let Some(texture) = title.texture()
|
|
||||||
{
|
|
||||||
let (x, y) = self.base.scale_point(x1, y1);
|
|
||||||
self.base.render_texture(
|
|
||||||
&texture,
|
|
||||||
None,
|
None,
|
||||||
x,
|
srgb,
|
||||||
y,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
self.base.scale,
|
|
||||||
Some(&bounds),
|
|
||||||
None,
|
|
||||||
AcquireSync::None,
|
|
||||||
ReleaseSync::None,
|
|
||||||
false,
|
|
||||||
srgb_srgb,
|
|
||||||
perceptual,
|
perceptual,
|
||||||
AlphaMode::PremultipliedElectrical,
|
scaled_cr,
|
||||||
|
bw as f32 * scalef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let body = Rect::new_sized_saturating(
|
let body = Rect::new_sized_saturating(
|
||||||
x + bw,
|
x + bw,
|
||||||
y + bw + tpuh,
|
y + bw,
|
||||||
pos.width() - 2 * bw,
|
pos.width() - 2 * bw,
|
||||||
pos.height() - 2 * bw - tpuh,
|
pos.height() - 2 * bw,
|
||||||
);
|
);
|
||||||
let scissor_body = self.base.scale_rect(body);
|
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));
|
child.node_render(self, body.x1(), body.y1(), Some(&scissor_body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ use {
|
||||||
},
|
},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt,
|
AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt,
|
||||||
GfxTexture, ReleaseSync, SampleRect,
|
GfxTexture, ReleaseSync, RoundedCopyTexture, RoundedFillRect, SampleRect,
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
theme::Color,
|
theme::{Color, CornerRadius},
|
||||||
tree::Transform,
|
tree::Transform,
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -250,6 +250,138 @@ 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,
|
||||||
|
) {
|
||||||
|
self.fill_rounded_rect_z(rect, color, alpha, cd, render_intent, corner_radius, border_width, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_rounded_rect_z(
|
||||||
|
&mut self,
|
||||||
|
rect: Rect,
|
||||||
|
color: &Color,
|
||||||
|
alpha: Option<f32>,
|
||||||
|
cd: &Rc<LinearColorDescription>,
|
||||||
|
render_intent: RenderIntent,
|
||||||
|
corner_radius: CornerRadius,
|
||||||
|
border_width: f32,
|
||||||
|
z_order: u32,
|
||||||
|
) {
|
||||||
|
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,
|
||||||
|
z_order,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
pub fn sync(&mut self) {
|
||||||
self.ops.push(GfxApiOpt::Sync);
|
self.ops.push(GfxApiOpt::Sync);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
src/state.rs
34
src/state.rs
|
|
@ -189,14 +189,13 @@ pub struct State {
|
||||||
pub theme: Theme,
|
pub theme: Theme,
|
||||||
pub pending_container_layout: AsyncQueue<Rc<ContainerNode>>,
|
pub pending_container_layout: AsyncQueue<Rc<ContainerNode>>,
|
||||||
pub pending_container_render_positions: 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_output_render_data: AsyncQueue<Rc<OutputNode>>,
|
||||||
pub pending_float_layout: AsyncQueue<Rc<FloatNode>>,
|
pub pending_float_layout: AsyncQueue<Rc<FloatNode>>,
|
||||||
pub pending_float_titles: AsyncQueue<Rc<FloatNode>>,
|
|
||||||
pub pending_input_popup_positioning: AsyncQueue<Rc<ZwpInputPopupSurfaceV2>>,
|
pub pending_input_popup_positioning: AsyncQueue<Rc<ZwpInputPopupSurfaceV2>>,
|
||||||
pub pending_toplevel_screencasts: AsyncQueue<Rc<JayScreencast>>,
|
pub pending_toplevel_screencasts: AsyncQueue<Rc<JayScreencast>>,
|
||||||
pub pending_screencast_reallocs_or_reconfigures: AsyncQueue<Rc<JayScreencast>>,
|
pub pending_screencast_reallocs_or_reconfigures: AsyncQueue<Rc<JayScreencast>>,
|
||||||
pub pending_placeholder_render_textures: AsyncQueue<Rc<PlaceholderNode>>,
|
pub pending_placeholder_render_textures: AsyncQueue<Rc<PlaceholderNode>>,
|
||||||
|
pub pending_container_tab_render_textures: AsyncQueue<Rc<ContainerNode>>,
|
||||||
pub dbus: Dbus,
|
pub dbus: Dbus,
|
||||||
pub fdcloser: Arc<FdCloser>,
|
pub fdcloser: Arc<FdCloser>,
|
||||||
pub logger: Option<Arc<Logger>>,
|
pub logger: Option<Arc<Logger>>,
|
||||||
|
|
@ -275,6 +274,7 @@ pub struct State {
|
||||||
pub color_manager: Rc<ColorManager>,
|
pub color_manager: Rc<ColorManager>,
|
||||||
pub float_above_fullscreen: Cell<bool>,
|
pub float_above_fullscreen: Cell<bool>,
|
||||||
pub icons: Icons,
|
pub icons: Icons,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub show_pin_icon: Cell<bool>,
|
pub show_pin_icon: Cell<bool>,
|
||||||
pub cl_matcher_manager: ClMatcherManager,
|
pub cl_matcher_manager: ClMatcherManager,
|
||||||
pub tl_matcher_manager: TlMatcherManager,
|
pub tl_matcher_manager: TlMatcherManager,
|
||||||
|
|
@ -550,10 +550,6 @@ impl DrmDevData {
|
||||||
struct UpdateTextTexturesVisitor;
|
struct UpdateTextTexturesVisitor;
|
||||||
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
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);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
|
@ -561,8 +557,6 @@ impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
node.title_textures.borrow_mut().clear();
|
|
||||||
node.schedule_render_titles();
|
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||||
|
|
@ -685,10 +679,6 @@ impl State {
|
||||||
struct Walker;
|
struct Walker;
|
||||||
impl NodeVisitorBase for Walker {
|
impl NodeVisitorBase for Walker {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
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);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||||
|
|
@ -702,7 +692,6 @@ impl State {
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
node.title_textures.borrow_mut().clear();
|
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
|
@ -1118,14 +1107,13 @@ impl State {
|
||||||
self.dbus.clear();
|
self.dbus.clear();
|
||||||
self.pending_container_layout.clear();
|
self.pending_container_layout.clear();
|
||||||
self.pending_container_render_positions.clear();
|
self.pending_container_render_positions.clear();
|
||||||
self.pending_container_render_title.clear();
|
|
||||||
self.pending_output_render_data.clear();
|
self.pending_output_render_data.clear();
|
||||||
self.pending_float_layout.clear();
|
self.pending_float_layout.clear();
|
||||||
self.pending_float_titles.clear();
|
|
||||||
self.pending_input_popup_positioning.clear();
|
self.pending_input_popup_positioning.clear();
|
||||||
self.pending_toplevel_screencasts.clear();
|
self.pending_toplevel_screencasts.clear();
|
||||||
self.pending_screencast_reallocs_or_reconfigures.clear();
|
self.pending_screencast_reallocs_or_reconfigures.clear();
|
||||||
self.pending_placeholder_render_textures.clear();
|
self.pending_placeholder_render_textures.clear();
|
||||||
|
self.pending_container_tab_render_textures.clear();
|
||||||
self.render_ctx_watchers.clear();
|
self.render_ctx_watchers.clear();
|
||||||
self.workspace_watchers.clear();
|
self.workspace_watchers.clear();
|
||||||
self.toplevel_lists.clear();
|
self.toplevel_lists.clear();
|
||||||
|
|
@ -1296,8 +1284,8 @@ impl State {
|
||||||
let (width, height) = target.logical_size(target_transform);
|
let (width, height) = target.logical_size(target_transform);
|
||||||
Rect::new_sized_saturating(0, 0, width, height)
|
Rect::new_sized_saturating(0, 0, width, height)
|
||||||
},
|
},
|
||||||
icons: None,
|
|
||||||
stretch: None,
|
stretch: None,
|
||||||
|
corner_radius: None,
|
||||||
};
|
};
|
||||||
let mut sample_rect = SampleRect::identity();
|
let mut sample_rect = SampleRect::identity();
|
||||||
sample_rect.buffer_transform = transform;
|
sample_rect.buffer_transform = transform;
|
||||||
|
|
@ -1621,8 +1609,7 @@ impl State {
|
||||||
.unwrap_or(c);
|
.unwrap_or(c);
|
||||||
Some(target.predict_child_body_size())
|
Some(target.predict_child_body_size())
|
||||||
} else {
|
} else {
|
||||||
let tpuh = self.theme.title_plus_underline_height();
|
Some((pos.width(), pos.height()))
|
||||||
Some((pos.width(), (pos.height() - tpuh).max(0)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1863,11 +1850,13 @@ impl State {
|
||||||
self.spaces_changed();
|
self.spaces_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_show_titles(&self, show: bool) {
|
pub fn set_show_titles(&self, show: bool) {
|
||||||
self.theme.show_titles.set(show);
|
self.theme.show_titles.set(show);
|
||||||
self.spaces_changed();
|
self.spaces_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_floating_titles(&self, floating: bool) {
|
pub fn set_floating_titles(&self, floating: bool) {
|
||||||
self.theme.floating_titles.set(floating);
|
self.theme.floating_titles.set(floating);
|
||||||
self.spaces_changed();
|
self.spaces_changed();
|
||||||
|
|
@ -1882,13 +1871,9 @@ impl State {
|
||||||
.set(threshold.saturating_mul(threshold));
|
.set(threshold.saturating_mul(threshold));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_show_pin_icon(&self, show: bool) {
|
pub fn set_show_pin_icon(&self, show: bool) {
|
||||||
self.show_pin_icon.set(show);
|
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) {
|
pub fn set_float_above_fullscreen(&self, v: bool) {
|
||||||
|
|
@ -1909,7 +1894,6 @@ impl State {
|
||||||
struct V;
|
struct V;
|
||||||
impl NodeVisitorBase for V {
|
impl NodeVisitorBase for V {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
node.schedule_render_titles();
|
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
|
@ -1917,7 +1901,6 @@ impl State {
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
node.schedule_render_titles();
|
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1943,6 +1926,7 @@ impl State {
|
||||||
self.fonts_changed();
|
self.fonts_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_title_font(&self, font: Option<&str>) {
|
pub fn set_title_font(&self, font: Option<&str>) {
|
||||||
let font = font.map(|font| Arc::new(font.to_string()));
|
let font = font.map(|font| Arc::new(font.to_string()));
|
||||||
self.theme.title_font.set(font);
|
self.theme.title_font.set(font);
|
||||||
|
|
|
||||||
|
|
@ -415,6 +415,7 @@ impl Shared {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(PartialEq, Default)]
|
#[derive(PartialEq, Default)]
|
||||||
enum Config<'a> {
|
enum Config<'a> {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -519,6 +520,7 @@ impl TextTexture {
|
||||||
self.data.pending_render.set(Some(pending));
|
self.data.pending_render.set(Some(pending));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn schedule_render(
|
pub fn schedule_render(
|
||||||
&self,
|
&self,
|
||||||
on_completed: Rc<dyn OnCompleted>,
|
on_completed: Rc<dyn OnCompleted>,
|
||||||
|
|
|
||||||
171
src/theme.rs
171
src/theme.rs
|
|
@ -454,6 +454,14 @@ colors! {
|
||||||
bar_text = (0xff, 0xff, 0xff),
|
bar_text = (0xff, 0xff, 0xff),
|
||||||
attention_requested_background = (0x23, 0x09, 0x2c),
|
attention_requested_background = (0x23, 0x09, 0x2c),
|
||||||
highlight = (0x9d, 0x28, 0xc6, 0x7f),
|
highlight = (0x9d, 0x28, 0xc6, 0x7f),
|
||||||
|
tab_active_background = (0x4c, 0x78, 0x99),
|
||||||
|
tab_active_border = (0x28, 0x55, 0x77),
|
||||||
|
tab_inactive_background = (0x22, 0x22, 0x22),
|
||||||
|
tab_inactive_border = (0x33, 0x33, 0x33),
|
||||||
|
tab_active_text = (0xff, 0xff, 0xff),
|
||||||
|
tab_inactive_text = (0x88, 0x88, 0x88),
|
||||||
|
tab_bar_background = (0x00, 0x00, 0x00, 0x00),
|
||||||
|
tab_attention_background = (0x23, 0x09, 0x2c),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticText for ThemeColor {
|
impl StaticText for ThemeColor {
|
||||||
|
|
@ -476,6 +484,14 @@ impl StaticText for ThemeColor {
|
||||||
ThemeColor::bar_text => "Bar Text",
|
ThemeColor::bar_text => "Bar Text",
|
||||||
ThemeColor::attention_requested_background => "Attention Requested",
|
ThemeColor::attention_requested_background => "Attention Requested",
|
||||||
ThemeColor::highlight => "Highlight",
|
ThemeColor::highlight => "Highlight",
|
||||||
|
ThemeColor::tab_active_background => "Tab Background (active)",
|
||||||
|
ThemeColor::tab_active_border => "Tab Border (active)",
|
||||||
|
ThemeColor::tab_inactive_background => "Tab Background (inactive)",
|
||||||
|
ThemeColor::tab_inactive_border => "Tab Border (inactive)",
|
||||||
|
ThemeColor::tab_active_text => "Tab Text (active)",
|
||||||
|
ThemeColor::tab_inactive_text => "Tab Text (inactive)",
|
||||||
|
ThemeColor::tab_bar_background => "Tab Bar Background",
|
||||||
|
ThemeColor::tab_attention_background => "Tab Attention Background",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -588,6 +604,12 @@ sizes! {
|
||||||
bar_separator_width = (0, 1000, 1),
|
bar_separator_width = (0, 1000, 1),
|
||||||
gap = (0, 1000, 0),
|
gap = (0, 1000, 0),
|
||||||
title_gap = (0, 1000, 5),
|
title_gap = (0, 1000, 5),
|
||||||
|
tab_bar_height = (0, 1000, 22),
|
||||||
|
tab_bar_padding = (0, 1000, 6),
|
||||||
|
tab_bar_radius = (0, 1000, 6),
|
||||||
|
tab_bar_border_width = (0, 1000, 2),
|
||||||
|
tab_bar_text_padding = (0, 1000, 4),
|
||||||
|
tab_bar_gap = (0, 1000, 4),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticText for ThemeSized {
|
impl StaticText for ThemeSized {
|
||||||
|
|
@ -599,6 +621,12 @@ impl StaticText for ThemeSized {
|
||||||
ThemeSized::bar_separator_width => "Bar Separator Width",
|
ThemeSized::bar_separator_width => "Bar Separator Width",
|
||||||
ThemeSized::gap => "Gap",
|
ThemeSized::gap => "Gap",
|
||||||
ThemeSized::title_gap => "Title Gap",
|
ThemeSized::title_gap => "Title Gap",
|
||||||
|
ThemeSized::tab_bar_height => "Tab Bar Height",
|
||||||
|
ThemeSized::tab_bar_padding => "Tab Bar Padding",
|
||||||
|
ThemeSized::tab_bar_radius => "Tab Bar Radius",
|
||||||
|
ThemeSized::tab_bar_border_width => "Tab Bar Border Width",
|
||||||
|
ThemeSized::tab_bar_text_padding => "Tab Bar Text Padding",
|
||||||
|
ThemeSized::tab_bar_gap => "Tab Bar Gap",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -643,6 +671,104 @@ 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Horizontal alignment of title text inside tab buttons.
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum TabTitleAlign {
|
||||||
|
#[default]
|
||||||
|
Start,
|
||||||
|
Center,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub colors: ThemeColors,
|
pub colors: ThemeColors,
|
||||||
pub sizes: ThemeSizes,
|
pub sizes: ThemeSizes,
|
||||||
|
|
@ -650,9 +776,14 @@ pub struct Theme {
|
||||||
pub bar_font: CloneCell<Option<Arc<String>>>,
|
pub bar_font: CloneCell<Option<Arc<String>>>,
|
||||||
pub title_font: CloneCell<Option<Arc<String>>>,
|
pub title_font: CloneCell<Option<Arc<String>>>,
|
||||||
pub default_font: Arc<String>,
|
pub default_font: Arc<String>,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub show_titles: Cell<bool>,
|
pub show_titles: Cell<bool>,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub floating_titles: Cell<bool>,
|
pub floating_titles: Cell<bool>,
|
||||||
pub bar_position: Cell<BarPosition>,
|
pub bar_position: Cell<BarPosition>,
|
||||||
|
pub corner_radius: Cell<CornerRadius>,
|
||||||
|
pub autotile_enabled: Cell<bool>,
|
||||||
|
pub tab_title_align: Cell<TabTitleAlign>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Theme {
|
impl Default for Theme {
|
||||||
|
|
@ -668,50 +799,28 @@ impl Default for Theme {
|
||||||
show_titles: Cell::new(true),
|
show_titles: Cell::new(true),
|
||||||
floating_titles: Cell::new(false),
|
floating_titles: Cell::new(false),
|
||||||
bar_position: Default::default(),
|
bar_position: Default::default(),
|
||||||
|
corner_radius: Cell::new(CornerRadius::default()),
|
||||||
|
autotile_enabled: Cell::new(false),
|
||||||
|
tab_title_align: Cell::new(TabTitleAlign::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
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> {
|
pub fn bar_font(&self) -> Arc<String> {
|
||||||
self.bar_font.get().unwrap_or_else(|| self.font.get())
|
self.bar_font.get().unwrap_or_else(|| self.font.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn title_font(&self) -> Arc<String> {
|
||||||
|
self.title_font.get().unwrap_or_else(|| self.font.get())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn title_height(&self) -> i32 {
|
pub fn title_height(&self) -> i32 {
|
||||||
if self.show_titles.get() {
|
0
|
||||||
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 {
|
pub fn title_plus_underline_height(&self) -> i32 {
|
||||||
if self.show_titles.get() {
|
0
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ mod float;
|
||||||
mod output;
|
mod output;
|
||||||
mod placeholder;
|
mod placeholder;
|
||||||
mod stacked;
|
mod stacked;
|
||||||
|
pub mod tab_bar;
|
||||||
mod toplevel;
|
mod toplevel;
|
||||||
mod walker;
|
mod walker;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -10,27 +10,22 @@ use {
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
|
||||||
state::State,
|
state::State,
|
||||||
text::TextTexture,
|
|
||||||
tree::{
|
tree::{
|
||||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||||
NodeLayerLink, NodeLocation, OutputNode, PinnedNode, StackedNode, TileDragDestination,
|
NodeLayerLink, NodeLocation, OutputNode, PinnedNode, StackedNode, TileDragDestination,
|
||||||
ToplevelNode, WorkspaceNode, toplevel_set_floating, walker::NodeVisitor,
|
ToplevelNode, WorkspaceNode, toplevel_set_floating, walker::NodeVisitor,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, double_click_state::DoubleClickState,
|
clonecell::CloneCell, double_click_state::DoubleClickState,
|
||||||
errorfmt::ErrorFmt, event_listener::LazyEventSource, linkedlist::LinkedNode,
|
event_listener::LazyEventSource, linkedlist::LinkedNode,
|
||||||
on_drop_event::OnDropEvent, smallmap::SmallMapMut,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
arrayvec::ArrayVec,
|
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
mem,
|
||||||
ops::Deref,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -50,10 +45,6 @@ pub struct FloatNode {
|
||||||
pub active: Cell<bool>,
|
pub active: Cell<bool>,
|
||||||
pub seat_state: NodeSeatState,
|
pub seat_state: NodeSeatState,
|
||||||
pub layout_scheduled: Cell<bool>,
|
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>>,
|
cursors: RefCell<AHashMap<CursorType, CursorState>>,
|
||||||
pub attention_requested: Cell<bool>,
|
pub attention_requested: Cell<bool>,
|
||||||
pub layout_complete: Rc<LazyEventSource>,
|
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 {
|
impl FloatNode {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
state: &Rc<State>,
|
state: &Rc<State>,
|
||||||
|
|
@ -131,10 +111,6 @@ impl FloatNode {
|
||||||
active: Cell::new(false),
|
active: Cell::new(false),
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
layout_scheduled: Cell::new(false),
|
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(),
|
cursors: Default::default(),
|
||||||
attention_requested: Cell::new(false),
|
attention_requested: Cell::new(false),
|
||||||
layout_complete: state.post_layout_event_sources.create_source(),
|
layout_complete: state.post_layout_event_sources.create_source(),
|
||||||
|
|
@ -162,7 +138,7 @@ impl FloatNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_colors_changed(self: &Rc<Self>) {
|
pub fn on_colors_changed(self: &Rc<Self>) {
|
||||||
self.schedule_render_titles();
|
// No title rendering needed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule_layout(self: &Rc<Self>) {
|
pub fn schedule_layout(self: &Rc<Self>) {
|
||||||
|
|
@ -179,95 +155,17 @@ impl FloatNode {
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let bw = theme.sizes.border_width.get();
|
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(
|
let cpos = Rect::new_sized_saturating(
|
||||||
pos.x1() + bw,
|
pos.x1() + bw,
|
||||||
pos.y1() + bw + tpuh,
|
pos.y1() + bw,
|
||||||
pos.width() - 2 * 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);
|
child.clone().tl_change_extents(&cpos);
|
||||||
self.title_rect.set(tr);
|
|
||||||
self.layout_scheduled.set(false);
|
self.layout_scheduled.set(false);
|
||||||
self.schedule_render_titles();
|
|
||||||
self.layout_complete.trigger();
|
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(
|
fn pointer_move(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
id: CursorType,
|
id: CursorType,
|
||||||
|
|
@ -280,7 +178,6 @@ impl FloatNode {
|
||||||
let y = y.round_down();
|
let y = y.round_down();
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let tpuh = theme.title_plus_underline_height();
|
|
||||||
let mut seats = self.cursors.borrow_mut();
|
let mut seats = self.cursors.borrow_mut();
|
||||||
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
let seat_state = seats.entry(id).or_insert_with(|| CursorState {
|
||||||
cursor: KnownCursor::Default,
|
cursor: KnownCursor::Default,
|
||||||
|
|
@ -316,7 +213,7 @@ impl FloatNode {
|
||||||
}
|
}
|
||||||
OpType::ResizeTop => {
|
OpType::ResizeTop => {
|
||||||
y1 += y - seat_state.dist_ver;
|
y1 += y - seat_state.dist_ver;
|
||||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
y1 = y1.min(y2 - 2 * bw);
|
||||||
}
|
}
|
||||||
OpType::ResizeRight => {
|
OpType::ResizeRight => {
|
||||||
x2 += x - pos.width() + seat_state.dist_hor;
|
x2 += x - pos.width() + seat_state.dist_hor;
|
||||||
|
|
@ -324,31 +221,31 @@ impl FloatNode {
|
||||||
}
|
}
|
||||||
OpType::ResizeBottom => {
|
OpType::ResizeBottom => {
|
||||||
y2 += y - pos.height() + seat_state.dist_ver;
|
y2 += y - pos.height() + seat_state.dist_ver;
|
||||||
y2 = y2.max(y1 + 2 * bw + tpuh);
|
y2 = y2.max(y1 + 2 * bw);
|
||||||
}
|
}
|
||||||
OpType::ResizeTopLeft => {
|
OpType::ResizeTopLeft => {
|
||||||
x1 += x - seat_state.dist_hor;
|
x1 += x - seat_state.dist_hor;
|
||||||
y1 += y - seat_state.dist_ver;
|
y1 += y - seat_state.dist_ver;
|
||||||
x1 = x1.min(x2 - 2 * bw);
|
x1 = x1.min(x2 - 2 * bw);
|
||||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
y1 = y1.min(y2 - 2 * bw);
|
||||||
}
|
}
|
||||||
OpType::ResizeTopRight => {
|
OpType::ResizeTopRight => {
|
||||||
x2 += x - pos.width() + seat_state.dist_hor;
|
x2 += x - pos.width() + seat_state.dist_hor;
|
||||||
y1 += y - seat_state.dist_ver;
|
y1 += y - seat_state.dist_ver;
|
||||||
x2 = x2.max(x1 + 2 * bw);
|
x2 = x2.max(x1 + 2 * bw);
|
||||||
y1 = y1.min(y2 - 2 * bw - tpuh);
|
y1 = y1.min(y2 - 2 * bw);
|
||||||
}
|
}
|
||||||
OpType::ResizeBottomLeft => {
|
OpType::ResizeBottomLeft => {
|
||||||
x1 += x - seat_state.dist_hor;
|
x1 += x - seat_state.dist_hor;
|
||||||
y2 += y - pos.height() + seat_state.dist_ver;
|
y2 += y - pos.height() + seat_state.dist_ver;
|
||||||
x1 = x1.min(x2 - 2 * bw);
|
x1 = x1.min(x2 - 2 * bw);
|
||||||
y2 = y2.max(y1 + 2 * bw + tpuh);
|
y2 = y2.max(y1 + 2 * bw);
|
||||||
}
|
}
|
||||||
OpType::ResizeBottomRight => {
|
OpType::ResizeBottomRight => {
|
||||||
x2 += x - pos.width() + seat_state.dist_hor;
|
x2 += x - pos.width() + seat_state.dist_hor;
|
||||||
y2 += y - pos.height() + seat_state.dist_ver;
|
y2 += y - pos.height() + seat_state.dist_ver;
|
||||||
x2 = x2.max(x1 + 2 * bw);
|
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);
|
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||||
|
|
@ -474,18 +371,12 @@ impl FloatNode {
|
||||||
self.schedule_layout();
|
self.schedule_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_child_title(self: &Rc<Self>, title: &str) {
|
fn update_child_title(self: &Rc<Self>, _title: &str) {
|
||||||
let mut t = self.title.borrow_mut();
|
// No title rendering
|
||||||
if t.deref() != title {
|
|
||||||
t.clear();
|
|
||||||
t.push_str(title);
|
|
||||||
self.schedule_render_titles();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_child_active(self: &Rc<Self>, active: bool) {
|
fn update_child_active(self: &Rc<Self>, active: bool) {
|
||||||
if self.active.replace(active) != active {
|
if self.active.replace(active) != active {
|
||||||
self.schedule_render_titles();
|
|
||||||
if active {
|
if active {
|
||||||
self.restack();
|
self.restack();
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +433,6 @@ impl FloatNode {
|
||||||
if let Some(tl) = self.child.get() {
|
if let Some(tl) = self.child.get() {
|
||||||
tl.tl_data().pinned.set(pl.is_some());
|
tl.tl_data().pinned.set(pl.is_some());
|
||||||
}
|
}
|
||||||
self.schedule_render_titles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button(
|
fn button(
|
||||||
|
|
@ -558,34 +448,6 @@ impl FloatNode {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
_ => return,
|
_ => 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 !cursor_data.op_active {
|
||||||
if !pressed {
|
if !pressed {
|
||||||
return;
|
return;
|
||||||
|
|
@ -601,7 +463,6 @@ impl FloatNode {
|
||||||
cursor_data.x,
|
cursor_data.x,
|
||||||
cursor_data.y,
|
cursor_data.y,
|
||||||
) && cursor_data.op_type == OpType::Move
|
) && cursor_data.op_type == OpType::Move
|
||||||
&& !is_icon_press
|
|
||||||
&& let Some(tl) = self.child.get()
|
&& let Some(tl) = self.child.get()
|
||||||
{
|
{
|
||||||
drop(cursors);
|
drop(cursors);
|
||||||
|
|
@ -653,11 +514,10 @@ impl FloatNode {
|
||||||
let child = self.child.get()?;
|
let child = self.child.get()?;
|
||||||
let theme = &self.state.theme.sizes;
|
let theme = &self.state.theme.sizes;
|
||||||
let bw = theme.border_width.get();
|
let bw = theme.border_width.get();
|
||||||
let tpuh = self.state.theme.title_plus_underline_height();
|
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
let body = Rect::new(
|
let body = Rect::new(
|
||||||
pos.x1() + bw,
|
pos.x1() + bw,
|
||||||
pos.y1() + bw + tpuh,
|
pos.y1() + bw,
|
||||||
pos.x2() - bw,
|
pos.x2() - bw,
|
||||||
pos.y2() - bw,
|
pos.y2() - bw,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -713,8 +573,8 @@ impl Node for FloatNode {
|
||||||
NodeLayerLink::Stacked(l)
|
NodeLayerLink::Stacked(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, title: &str) {
|
fn node_child_title_changed(self: Rc<Self>, _child: &dyn Node, _title: &str) {
|
||||||
self.update_child_title(title);
|
// No title rendering
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_accepts_focus(&self) -> bool {
|
fn node_accepts_focus(&self) -> bool {
|
||||||
|
|
@ -738,13 +598,12 @@ impl Node for FloatNode {
|
||||||
usecase: FindTreeUsecase,
|
usecase: FindTreeUsecase,
|
||||||
) -> FindTreeResult {
|
) -> FindTreeResult {
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let tpuh = theme.title_plus_underline_height();
|
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
if x < bw || x >= pos.width() - bw {
|
if x < bw || x >= pos.width() - bw {
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
if y < bw + tpuh || y >= pos.height() - bw {
|
if y < bw || y >= pos.height() - bw {
|
||||||
return FindTreeResult::AcceptsInput;
|
return FindTreeResult::AcceptsInput;
|
||||||
}
|
}
|
||||||
let child = match self.child.get() {
|
let child = match self.child.get() {
|
||||||
|
|
@ -752,7 +611,7 @@ impl Node for FloatNode {
|
||||||
_ => return FindTreeResult::Other,
|
_ => return FindTreeResult::Other,
|
||||||
};
|
};
|
||||||
let x = x - bw;
|
let x = x - bw;
|
||||||
let y = y - bw - tpuh;
|
let y = y - bw;
|
||||||
tree.push(FoundNode {
|
tree.push(FoundNode {
|
||||||
node: child.clone(),
|
node: child.clone(),
|
||||||
x,
|
x,
|
||||||
|
|
@ -934,9 +793,8 @@ impl ContainingNode for FloatNode {
|
||||||
|
|
||||||
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
|
fn cnode_set_child_position(self: Rc<Self>, _child: &dyn Node, x: i32, y: i32) {
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let tpuh = theme.title_plus_underline_height();
|
|
||||||
let bw = theme.sizes.border_width.get();
|
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();
|
let pos = self.position.get();
|
||||||
if pos.position() != (x, y) {
|
if pos.position() != (x, y) {
|
||||||
let new_pos = pos.at_point(x, y);
|
let new_pos = pos.at_point(x, y);
|
||||||
|
|
@ -956,7 +814,6 @@ impl ContainingNode for FloatNode {
|
||||||
new_y2: Option<i32>,
|
new_y2: Option<i32>,
|
||||||
) {
|
) {
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let tpuh = theme.title_plus_underline_height();
|
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
let mut x1 = pos.x1();
|
let mut x1 = pos.x1();
|
||||||
|
|
@ -970,10 +827,10 @@ impl ContainingNode for FloatNode {
|
||||||
x2 = (v + bw).max(x1 + bw + bw);
|
x2 = (v + bw).max(x1 + bw + bw);
|
||||||
}
|
}
|
||||||
if let Some(v) = new_y1 {
|
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 {
|
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);
|
let new_pos = Rect::new_saturating(x1, y1, x2, y2);
|
||||||
if new_pos != pos {
|
if new_pos != pos {
|
||||||
|
|
|
||||||
113
src/tree/tab_bar.rs
Normal file
113
src/tree/tab_bar.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
scale::Scale,
|
||||||
|
state::State,
|
||||||
|
text::TextTexture,
|
||||||
|
theme::Color,
|
||||||
|
tree::NodeId,
|
||||||
|
},
|
||||||
|
std::{cell::{Cell, RefCell}, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single entry (tab) within a tab bar.
|
||||||
|
pub struct TabBarEntry {
|
||||||
|
/// The node ID of the child this tab represents.
|
||||||
|
pub child_id: NodeId,
|
||||||
|
/// The display title of the tab.
|
||||||
|
pub title: String,
|
||||||
|
/// Pre-rendered text texture for the tab title.
|
||||||
|
pub title_texture: Rc<RefCell<Option<TextTexture>>>,
|
||||||
|
/// Whether this is the active (visible) tab.
|
||||||
|
pub active: bool,
|
||||||
|
/// Whether this tab's window has requested attention.
|
||||||
|
pub attention_requested: bool,
|
||||||
|
/// X offset of this tab within the tab bar (relative to tab bar start).
|
||||||
|
pub x: Cell<i32>,
|
||||||
|
/// Width of this tab in pixels.
|
||||||
|
pub width: Cell<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A tab bar rendered above a container in mono (tabbed) mode.
|
||||||
|
pub struct TabBar {
|
||||||
|
/// The individual tab entries.
|
||||||
|
pub entries: Vec<TabBarEntry>,
|
||||||
|
/// Height of the tab bar in pixels (from theme).
|
||||||
|
pub height: i32,
|
||||||
|
/// The output scale at which text textures were rendered.
|
||||||
|
pub render_scale: Scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabBar {
|
||||||
|
/// Create a new empty tab bar.
|
||||||
|
pub fn new(height: i32, render_scale: Scale) -> Self {
|
||||||
|
Self {
|
||||||
|
entries: Vec::new(),
|
||||||
|
height,
|
||||||
|
render_scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recompute the positions and widths of all tab entries.
|
||||||
|
///
|
||||||
|
/// `total_width` is the available width for the entire tab bar.
|
||||||
|
pub fn layout_entries(&self, total_width: i32, padding: i32) {
|
||||||
|
let n = self.entries.len() as i32;
|
||||||
|
if n == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let total_padding = padding * (n + 1);
|
||||||
|
let available = (total_width - total_padding).max(0);
|
||||||
|
let per_tab = available / n;
|
||||||
|
let mut remainder = available - per_tab * n;
|
||||||
|
let mut x = padding;
|
||||||
|
for entry in &self.entries {
|
||||||
|
let w = if remainder > 0 {
|
||||||
|
remainder -= 1;
|
||||||
|
per_tab + 1
|
||||||
|
} else {
|
||||||
|
per_tab
|
||||||
|
};
|
||||||
|
entry.x.set(x);
|
||||||
|
entry.width.set(w);
|
||||||
|
x += w + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the tab entry index at the given x coordinate (relative to tab bar).
|
||||||
|
///
|
||||||
|
/// Returns `None` if the coordinate is in padding between tabs or out of bounds.
|
||||||
|
pub fn entry_at_x(&self, x: i32) -> Option<usize> {
|
||||||
|
for (i, entry) in self.entries.iter().enumerate() {
|
||||||
|
let ex = entry.x.get();
|
||||||
|
let ew = entry.width.get();
|
||||||
|
if x >= ex && x < ex + ew {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the colors for a tab entry based on its state.
|
||||||
|
pub fn entry_colors(state: &State, entry: &TabBarEntry) -> (Color, Color, Color) {
|
||||||
|
let theme = &state.theme;
|
||||||
|
if entry.attention_requested {
|
||||||
|
(
|
||||||
|
theme.colors.tab_attention_background.get(),
|
||||||
|
theme.colors.tab_inactive_border.get(),
|
||||||
|
theme.colors.tab_active_text.get(),
|
||||||
|
)
|
||||||
|
} else if entry.active {
|
||||||
|
(
|
||||||
|
theme.colors.tab_active_background.get(),
|
||||||
|
theme.colors.tab_active_border.get(),
|
||||||
|
theme.colors.tab_active_text.get(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
theme.colors.tab_inactive_background.get(),
|
||||||
|
theme.colors.tab_inactive_border.get(),
|
||||||
|
theme.colors.tab_inactive_text.get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
Axis, Direction, Workspace,
|
Direction, Workspace,
|
||||||
client::ClientCapabilities,
|
client::ClientCapabilities,
|
||||||
input::{
|
input::{
|
||||||
FallbackOutputMode, LayerDirection, SwitchEvent, Timeline, acceleration::AccelProfile,
|
FallbackOutputMode, LayerDirection, SwitchEvent, Timeline, acceleration::AccelProfile,
|
||||||
|
|
@ -60,15 +60,10 @@ pub enum SimpleCommand {
|
||||||
Quit,
|
Quit,
|
||||||
ReloadConfigSo,
|
ReloadConfigSo,
|
||||||
ReloadConfigToml,
|
ReloadConfigToml,
|
||||||
Split(Axis),
|
|
||||||
ToggleFloating,
|
ToggleFloating,
|
||||||
SetFloating(bool),
|
SetFloating(bool),
|
||||||
ToggleFullscreen,
|
ToggleFullscreen,
|
||||||
SetFullscreen(bool),
|
SetFullscreen(bool),
|
||||||
ToggleMono,
|
|
||||||
SetMono(bool),
|
|
||||||
ToggleSplit,
|
|
||||||
SetSplit(Axis),
|
|
||||||
Forward(bool),
|
Forward(bool),
|
||||||
EnableWindowManagement(bool),
|
EnableWindowManagement(bool),
|
||||||
SetFloatAboveFullscreen(bool),
|
SetFloatAboveFullscreen(bool),
|
||||||
|
|
@ -94,6 +89,17 @@ pub enum SimpleCommand {
|
||||||
ReloadSimpleIm,
|
ReloadSimpleIm,
|
||||||
EnableUnicodeInput,
|
EnableUnicodeInput,
|
||||||
WarpMouseToFocus,
|
WarpMouseToFocus,
|
||||||
|
ToggleTab,
|
||||||
|
MakeGroupH,
|
||||||
|
MakeGroupV,
|
||||||
|
MakeGroupTab,
|
||||||
|
ChangeGroupOpposite,
|
||||||
|
Equalize,
|
||||||
|
EqualizeRecursive,
|
||||||
|
MoveTabLeft,
|
||||||
|
MoveTabRight,
|
||||||
|
SetAutotile(bool),
|
||||||
|
ToggleAutotile,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -228,6 +234,22 @@ pub struct Theme {
|
||||||
pub gap: Option<i32>,
|
pub gap: Option<i32>,
|
||||||
pub floating_titles: Option<bool>,
|
pub floating_titles: Option<bool>,
|
||||||
pub title_gap: Option<i32>,
|
pub title_gap: Option<i32>,
|
||||||
|
pub corner_radius: Option<f32>,
|
||||||
|
pub tab_active_bg_color: Option<Color>,
|
||||||
|
pub tab_active_border_color: Option<Color>,
|
||||||
|
pub tab_inactive_bg_color: Option<Color>,
|
||||||
|
pub tab_inactive_border_color: Option<Color>,
|
||||||
|
pub tab_active_text_color: Option<Color>,
|
||||||
|
pub tab_inactive_text_color: Option<Color>,
|
||||||
|
pub tab_bar_bg_color: Option<Color>,
|
||||||
|
pub tab_attention_bg_color: Option<Color>,
|
||||||
|
pub tab_bar_height: Option<i32>,
|
||||||
|
pub tab_bar_padding: Option<i32>,
|
||||||
|
pub tab_bar_radius: Option<i32>,
|
||||||
|
pub tab_bar_border_width: Option<i32>,
|
||||||
|
pub tab_bar_text_padding: Option<i32>,
|
||||||
|
pub tab_bar_gap: Option<i32>,
|
||||||
|
pub tab_title_align: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ use {
|
||||||
},
|
},
|
||||||
indexmap::IndexMap,
|
indexmap::IndexMap,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
Axis::{Horizontal, Vertical},
|
|
||||||
Direction, get_workspace,
|
Direction, get_workspace,
|
||||||
input::{LayerDirection, Timeline},
|
input::{LayerDirection, Timeline},
|
||||||
},
|
},
|
||||||
|
|
@ -115,14 +114,6 @@ impl ActionParser<'_> {
|
||||||
"move-down" => Move(Down),
|
"move-down" => Move(Down),
|
||||||
"move-up" => Move(Up),
|
"move-up" => Move(Up),
|
||||||
"move-right" => Move(Right),
|
"move-right" => Move(Right),
|
||||||
"split-horizontal" => Split(Horizontal),
|
|
||||||
"split-vertical" => Split(Vertical),
|
|
||||||
"toggle-split" => ToggleSplit,
|
|
||||||
"tile-horizontal" => SetSplit(Horizontal),
|
|
||||||
"tile-vertical" => SetSplit(Vertical),
|
|
||||||
"toggle-mono" => ToggleMono,
|
|
||||||
"show-single" => SetMono(true),
|
|
||||||
"show-all" => SetMono(false),
|
|
||||||
"toggle-fullscreen" => ToggleFullscreen,
|
"toggle-fullscreen" => ToggleFullscreen,
|
||||||
"enter-fullscreen" => SetFullscreen(true),
|
"enter-fullscreen" => SetFullscreen(true),
|
||||||
"exit-fullscreen" => SetFullscreen(false),
|
"exit-fullscreen" => SetFullscreen(false),
|
||||||
|
|
@ -172,6 +163,18 @@ impl ActionParser<'_> {
|
||||||
"reload-simple-im" => ReloadSimpleIm,
|
"reload-simple-im" => ReloadSimpleIm,
|
||||||
"enable-unicode-input" => EnableUnicodeInput,
|
"enable-unicode-input" => EnableUnicodeInput,
|
||||||
"warp-mouse-to-focus" => WarpMouseToFocus,
|
"warp-mouse-to-focus" => WarpMouseToFocus,
|
||||||
|
"toggle-tab" => ToggleTab,
|
||||||
|
"make-group-h" => MakeGroupH,
|
||||||
|
"make-group-v" => MakeGroupV,
|
||||||
|
"make-group-tab" => MakeGroupTab,
|
||||||
|
"change-group-opposite" => ChangeGroupOpposite,
|
||||||
|
"equalize" => Equalize,
|
||||||
|
"equalize-recursive" => EqualizeRecursive,
|
||||||
|
"move-tab-left" => MoveTabLeft,
|
||||||
|
"move-tab-right" => MoveTabRight,
|
||||||
|
"enable-autotile" => SetAutotile(true),
|
||||||
|
"disable-autotile" => SetAutotile(false),
|
||||||
|
"toggle-autotile" => ToggleAutotile,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
config::{
|
config::{
|
||||||
Theme,
|
Theme,
|
||||||
context::Context,
|
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},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
parsers::color::ColorParser,
|
parsers::color::ColorParser,
|
||||||
},
|
},
|
||||||
|
|
@ -97,7 +97,45 @@ impl Parser for ThemeParser<'_> {
|
||||||
recover(opt(bol("floating-titles"))),
|
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"))),
|
||||||
|
))?;
|
||||||
|
let (
|
||||||
|
(
|
||||||
|
tab_active_bg_color,
|
||||||
|
tab_active_border_color,
|
||||||
|
tab_inactive_bg_color,
|
||||||
|
tab_inactive_border_color,
|
||||||
|
tab_active_text_color,
|
||||||
|
tab_inactive_text_color,
|
||||||
|
tab_bar_bg_color,
|
||||||
|
tab_attention_bg_color,
|
||||||
|
tab_bar_height,
|
||||||
|
tab_bar_padding,
|
||||||
|
),
|
||||||
|
(tab_bar_radius, tab_bar_border_width, tab_bar_text_padding, tab_bar_gap, tab_title_align_val),
|
||||||
|
) = ext.extract((
|
||||||
|
(
|
||||||
|
opt(val("tab-active-bg-color")),
|
||||||
|
opt(val("tab-active-border-color")),
|
||||||
|
opt(val("tab-inactive-bg-color")),
|
||||||
|
opt(val("tab-inactive-border-color")),
|
||||||
|
opt(val("tab-active-text-color")),
|
||||||
|
opt(val("tab-inactive-text-color")),
|
||||||
|
opt(val("tab-bar-bg-color")),
|
||||||
|
opt(val("tab-attention-bg-color")),
|
||||||
|
recover(opt(s32("tab-bar-height"))),
|
||||||
|
recover(opt(s32("tab-bar-padding"))),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
recover(opt(s32("tab-bar-radius"))),
|
||||||
|
recover(opt(s32("tab-bar-border-width"))),
|
||||||
|
recover(opt(s32("tab-bar-text-padding"))),
|
||||||
|
recover(opt(s32("tab-bar-gap"))),
|
||||||
|
recover(opt(str("tab-title-align"))),
|
||||||
|
),
|
||||||
|
))?;
|
||||||
macro_rules! color {
|
macro_rules! color {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
match $e {
|
match $e {
|
||||||
|
|
@ -152,6 +190,22 @@ impl Parser for ThemeParser<'_> {
|
||||||
gap: gap.despan(),
|
gap: gap.despan(),
|
||||||
floating_titles: floating_titles.despan(),
|
floating_titles: floating_titles.despan(),
|
||||||
title_gap: title_gap.despan(),
|
title_gap: title_gap.despan(),
|
||||||
|
corner_radius: corner_radius.map(|v| v.value as f32),
|
||||||
|
tab_active_bg_color: color!(tab_active_bg_color),
|
||||||
|
tab_active_border_color: color!(tab_active_border_color),
|
||||||
|
tab_inactive_bg_color: color!(tab_inactive_bg_color),
|
||||||
|
tab_inactive_border_color: color!(tab_inactive_border_color),
|
||||||
|
tab_active_text_color: color!(tab_active_text_color),
|
||||||
|
tab_inactive_text_color: color!(tab_inactive_text_color),
|
||||||
|
tab_bar_bg_color: color!(tab_bar_bg_color),
|
||||||
|
tab_attention_bg_color: color!(tab_attention_bg_color),
|
||||||
|
tab_bar_height: tab_bar_height.despan(),
|
||||||
|
tab_bar_padding: tab_bar_padding.despan(),
|
||||||
|
tab_bar_radius: tab_bar_radius.despan(),
|
||||||
|
tab_bar_border_width: tab_bar_border_width.despan(),
|
||||||
|
tab_bar_text_padding: tab_bar_text_padding.despan(),
|
||||||
|
tab_bar_gap: tab_bar_gap.despan(),
|
||||||
|
tab_title_align: tab_title_align_val.map(|v| v.value.to_string()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
ahash::{AHashMap, AHashSet},
|
ahash::{AHashMap, AHashSet},
|
||||||
error_reporter::Report,
|
error_reporter::Report,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
|
Axis,
|
||||||
client::Client,
|
client::Client,
|
||||||
config, config_dir,
|
config, config_dir,
|
||||||
exec::{Command, set_env, unset_env},
|
exec::{Command, set_env, unset_env},
|
||||||
|
|
@ -40,7 +41,7 @@ use {
|
||||||
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
||||||
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
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_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_autotile, set_tab_title_align,
|
||||||
set_ui_drag_enabled, set_ui_drag_threshold,
|
set_ui_drag_enabled, set_ui_drag_threshold,
|
||||||
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
status::{set_i3bar_separator, set_status, set_status_command, unset_status_command},
|
||||||
switch_to_vt,
|
switch_to_vt,
|
||||||
|
|
@ -169,11 +170,6 @@ impl Action {
|
||||||
Action::SimpleCommand { cmd } => match cmd {
|
Action::SimpleCommand { cmd } => match cmd {
|
||||||
SimpleCommand::Focus(dir) => b.new(move || s.focus(dir)),
|
SimpleCommand::Focus(dir) => b.new(move || s.focus(dir)),
|
||||||
SimpleCommand::Move(dir) => window_or_seat!(s, s.move_(dir)),
|
SimpleCommand::Move(dir) => window_or_seat!(s, s.move_(dir)),
|
||||||
SimpleCommand::Split(axis) => window_or_seat!(s, s.create_split(axis)),
|
|
||||||
SimpleCommand::ToggleSplit => window_or_seat!(s, s.toggle_split()),
|
|
||||||
SimpleCommand::SetSplit(b) => window_or_seat!(s, s.set_split(b)),
|
|
||||||
SimpleCommand::ToggleMono => window_or_seat!(s, s.toggle_mono()),
|
|
||||||
SimpleCommand::SetMono(b) => window_or_seat!(s, s.set_mono(b)),
|
|
||||||
SimpleCommand::ToggleFullscreen => window_or_seat!(s, s.toggle_fullscreen()),
|
SimpleCommand::ToggleFullscreen => window_or_seat!(s, s.toggle_fullscreen()),
|
||||||
SimpleCommand::SetFullscreen(b) => window_or_seat!(s, s.set_fullscreen(b)),
|
SimpleCommand::SetFullscreen(b) => window_or_seat!(s, s.set_fullscreen(b)),
|
||||||
SimpleCommand::FocusParent => b.new(move || s.focus_parent()),
|
SimpleCommand::FocusParent => b.new(move || s.focus_parent()),
|
||||||
|
|
@ -259,6 +255,35 @@ impl Action {
|
||||||
let persistent = state.persistent.clone();
|
let persistent = state.persistent.clone();
|
||||||
b.new(move || persistent.seat.warp_mouse_to_focus())
|
b.new(move || persistent.seat.warp_mouse_to_focus())
|
||||||
}
|
}
|
||||||
|
SimpleCommand::ToggleTab => b.new(move || s.toggle_tab()),
|
||||||
|
SimpleCommand::MakeGroupH => {
|
||||||
|
b.new(move || s.make_group(Axis::Horizontal, true))
|
||||||
|
}
|
||||||
|
SimpleCommand::MakeGroupV => {
|
||||||
|
b.new(move || s.make_group(Axis::Vertical, true))
|
||||||
|
}
|
||||||
|
SimpleCommand::MakeGroupTab => {
|
||||||
|
b.new(move || {
|
||||||
|
s.make_group(Axis::Horizontal, true);
|
||||||
|
s.toggle_tab();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
SimpleCommand::ChangeGroupOpposite => {
|
||||||
|
b.new(move || s.change_group_opposite())
|
||||||
|
}
|
||||||
|
SimpleCommand::Equalize => b.new(move || s.equalize(false)),
|
||||||
|
SimpleCommand::EqualizeRecursive => b.new(move || s.equalize(true)),
|
||||||
|
SimpleCommand::MoveTabLeft => b.new(move || s.move_tab(false)),
|
||||||
|
SimpleCommand::MoveTabRight => b.new(move || s.move_tab(true)),
|
||||||
|
SimpleCommand::SetAutotile(enabled) => {
|
||||||
|
b.new(move || set_autotile(enabled))
|
||||||
|
}
|
||||||
|
SimpleCommand::ToggleAutotile => {
|
||||||
|
b.new(move || {
|
||||||
|
// Toggle not directly supported; set to true
|
||||||
|
set_autotile(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||||
|
|
@ -997,6 +1022,14 @@ impl State {
|
||||||
color!(UNFOCUSED_TITLE_BACKGROUND_COLOR, unfocused_title_bg_color);
|
color!(UNFOCUSED_TITLE_BACKGROUND_COLOR, unfocused_title_bg_color);
|
||||||
color!(UNFOCUSED_TITLE_TEXT_COLOR, unfocused_title_text_color);
|
color!(UNFOCUSED_TITLE_TEXT_COLOR, unfocused_title_text_color);
|
||||||
color!(HIGHLIGHT_COLOR, highlight_color);
|
color!(HIGHLIGHT_COLOR, highlight_color);
|
||||||
|
color!(TAB_ACTIVE_BACKGROUND_COLOR, tab_active_bg_color);
|
||||||
|
color!(TAB_ACTIVE_BORDER_COLOR, tab_active_border_color);
|
||||||
|
color!(TAB_INACTIVE_BACKGROUND_COLOR, tab_inactive_bg_color);
|
||||||
|
color!(TAB_INACTIVE_BORDER_COLOR, tab_inactive_border_color);
|
||||||
|
color!(TAB_ACTIVE_TEXT_COLOR, tab_active_text_color);
|
||||||
|
color!(TAB_INACTIVE_TEXT_COLOR, tab_inactive_text_color);
|
||||||
|
color!(TAB_BAR_BACKGROUND_COLOR, tab_bar_bg_color);
|
||||||
|
color!(TAB_ATTENTION_BACKGROUND_COLOR, tab_attention_bg_color);
|
||||||
macro_rules! size {
|
macro_rules! size {
|
||||||
($sized:ident, $field:ident) => {
|
($sized:ident, $field:ident) => {
|
||||||
if let Some(size) = theme.$field {
|
if let Some(size) = theme.$field {
|
||||||
|
|
@ -1010,6 +1043,12 @@ impl State {
|
||||||
size!(BAR_SEPARATOR_WIDTH, bar_separator_width);
|
size!(BAR_SEPARATOR_WIDTH, bar_separator_width);
|
||||||
size!(GAP, gap);
|
size!(GAP, gap);
|
||||||
size!(TITLE_GAP, title_gap);
|
size!(TITLE_GAP, title_gap);
|
||||||
|
size!(TAB_BAR_HEIGHT, tab_bar_height);
|
||||||
|
size!(TAB_BAR_PADDING, tab_bar_padding);
|
||||||
|
size!(TAB_BAR_RADIUS, tab_bar_radius);
|
||||||
|
size!(TAB_BAR_BORDER_WIDTH, tab_bar_border_width);
|
||||||
|
size!(TAB_BAR_TEXT_PADDING, tab_bar_text_padding);
|
||||||
|
size!(TAB_BAR_GAP, tab_bar_gap);
|
||||||
macro_rules! font {
|
macro_rules! font {
|
||||||
($fun:ident, $field:ident) => {
|
($fun:ident, $field:ident) => {
|
||||||
if let Some(font) = &theme.$field {
|
if let Some(font) = &theme.$field {
|
||||||
|
|
@ -1020,6 +1059,12 @@ impl State {
|
||||||
font!(set_font, font);
|
font!(set_font, font);
|
||||||
font!(set_title_font, title_font);
|
font!(set_title_font, title_font);
|
||||||
font!(set_bar_font, bar_font);
|
font!(set_bar_font, bar_font);
|
||||||
|
if let Some(radius) = theme.corner_radius {
|
||||||
|
set_corner_radius(radius);
|
||||||
|
}
|
||||||
|
if let Some(ref align) = theme.tab_title_align {
|
||||||
|
set_tab_title_align(align);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_switch_device(self: &Rc<Self>, dev: InputDevice, actions: &Rc<SwitchActions>) {
|
fn handle_switch_device(self: &Rc<Self>, dev: InputDevice, actions: &Rc<SwitchActions>) {
|
||||||
|
|
|
||||||
|
|
@ -2426,6 +2426,11 @@ Theme:
|
||||||
kind: string
|
kind: string
|
||||||
required: false
|
required: false
|
||||||
description: The name of the font to use in the bar. Defaults to `font` if not set.
|
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:
|
Config:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue