feat(layout): add tabbed window groups and scrolling columns
This commit is contained in:
1 parent
7d58eeb65d
commit
bfa78f62f2
27 files changed
+1480
-47
No files matched your search
@@ -11,6 +11,7 @@ mod overview;
|
||||
mod resources;
|
||||
mod scene;
|
||||
mod shader;
|
||||
mod tabs;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -329,6 +330,8 @@ pub(crate) enum OverviewChromePart {
|
||||
Outline,
|
||||
Strip,
|
||||
Caption,
|
||||
Tab,
|
||||
TabActive,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -122,7 +122,9 @@ pub(super) fn overview_strip_elements(
|
||||
&& let Some(cache) = state.render.outputs.get_mut(&output_id)
|
||||
{
|
||||
cache.chrome.retain(|(id, part), _| match part {
|
||||
OverviewChromePart::Caption => state.windows.window(ferese_layout::WindowId(*id)).is_some(),
|
||||
OverviewChromePart::Caption | OverviewChromePart::Tab | OverviewChromePart::TabActive => {
|
||||
state.windows.window(ferese_layout::WindowId(*id)).is_some()
|
||||
}
|
||||
OverviewChromePart::Card | OverviewChromePart::Outline => card_ids.contains(id),
|
||||
OverviewChromePart::Strip => true,
|
||||
});
|
||||
|
||||
@@ -69,6 +69,11 @@ impl RenderResources {
|
||||
|
||||
pub fn remove_window(&mut self, id: WindowId) {
|
||||
self.windows.remove(&id);
|
||||
for output in self.outputs.values_mut() {
|
||||
output.chrome.retain(|(key, part), _| {
|
||||
*key != id.0 || !matches!(part, OverviewChromePart::Tab | OverviewChromePart::TabActive)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_output(&mut self, id: OutputId) {
|
||||
|
||||
@@ -195,6 +195,7 @@ fn scene_elements(
|
||||
if let Some(group) = closing.remove(&Some(id)) {
|
||||
elements.extend(group);
|
||||
}
|
||||
elements.extend(super::tabs::tab_elements(state, renderer, output, frame, id, capture));
|
||||
let constrain = rounded_visual_rect(visual, output_geometry.loc);
|
||||
let material_surface = window
|
||||
.toplevel()
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn tab_elements(
|
||||
state: &mut Ferese,
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
frame: &crate::state::FrameScene,
|
||||
active: ferese_layout::WindowId,
|
||||
capture: bool,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
if frame.overview.is_presenting() {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(workspace) = state.workspaces.workspace_for_window(active) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(bar) = state
|
||||
.visible_tab_bars(workspace)
|
||||
.find(|bar| bar.group.active == active)
|
||||
.cloned()
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(sample) = frame.windows.get(&active) else {
|
||||
return Vec::new();
|
||||
};
|
||||
if bar.rect.height <= 0.0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let content = sample.presentation.bounds.current;
|
||||
let alpha = sample.presentation.alpha();
|
||||
let Some(output_geometry) = state.space.output_geometry(output) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let width = content.width / bar.group.windows.len() as f64;
|
||||
let mut elements = Vec::new();
|
||||
state.retain_overview_labels();
|
||||
for (index, id) in bar.group.windows.iter().copied().enumerate() {
|
||||
let rect = ferese_layout::Rect::new(
|
||||
content.x + width * index as f64,
|
||||
content.y - bar.rect.height,
|
||||
width,
|
||||
bar.rect.height,
|
||||
);
|
||||
let protected = capture
|
||||
&& state
|
||||
.windows
|
||||
.window(id)
|
||||
.is_none_or(|window| state.capture_protected(window));
|
||||
if !protected
|
||||
&& width > 20.0
|
||||
&& let Some((buffer, size)) = state.overview_window_label(id, scale, width)
|
||||
{
|
||||
let location = Point::<i32, Physical>::from((
|
||||
((rect.x - f64::from(output_geometry.loc.x) + 8.0) * scale).round() as i32,
|
||||
((rect.y - f64::from(output_geometry.loc.y) + rect.height * 0.5) * scale).round() as i32 - size.h / 2,
|
||||
));
|
||||
if let Ok(element) = MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
location.to_f64(),
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(Rectangle::from_size((f64::from(size.w), f64::from(size.h)).into())),
|
||||
Some(
|
||||
(
|
||||
(f64::from(size.w) / scale).round() as i32,
|
||||
(f64::from(size.h) / scale).round() as i32,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
RenderElementKind::Unspecified,
|
||||
) {
|
||||
let element = RescaleRenderElement::from_element(element, location, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
let clip = ferese_layout::Rect::new(rect.x + 8.0, rect.y, (rect.width - 16.0).max(0.0), rect.height);
|
||||
if let Some(element) =
|
||||
CropRenderElement::from_element(element, scale, physical_rect(clip, output_geometry.loc, scale))
|
||||
{
|
||||
elements.push(element.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
if id == active {
|
||||
let thickness = rect.height.min(2.0);
|
||||
let underline = ferese_layout::Rect::new(rect.x, rect.y + rect.height - thickness, rect.width, thickness);
|
||||
if let Some(element) = overview_chrome_element(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
output_geometry,
|
||||
scale,
|
||||
(id.0, OverviewChromePart::TabActive),
|
||||
underline,
|
||||
color_with_alpha(state.theme_settings.accent_color.0, alpha),
|
||||
0.0,
|
||||
0.0,
|
||||
) {
|
||||
elements.push(element.into());
|
||||
}
|
||||
}
|
||||
let color = if id == active {
|
||||
state.theme_settings.surface_raised_color.0
|
||||
} else {
|
||||
state.theme_settings.surface_base_color.0
|
||||
};
|
||||
if let Some(element) = overview_chrome_element(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
output_geometry,
|
||||
scale,
|
||||
(id.0, OverviewChromePart::Tab),
|
||||
rect,
|
||||
color_with_alpha(color, alpha),
|
||||
0.0,
|
||||
0.0,
|
||||
) {
|
||||
elements.push(element.into());
|
||||
}
|
||||
}
|
||||
elements
|
||||
}
|
||||
Reference in new issue
Block a user