use super::tab::{Tab, TabBackgroundTheme, TabMessage, TabRuleTheme, MIN_ACTIVE_TAB_WIDTH}; use cosmic::{ font::Font, iced::{id::Id, widget, Element}, iced_core::{ event, layout::{Layout, Limits, Node}, mouse, overlay, renderer, widget::{ operation::{ scrollable::{AbsoluteOffset, RelativeOffset}, Operation, OperationOutputWrapper, Scrollable, }, text::StyleSheet as TextStyleSheet, tree::{self, Tree}, Widget, }, Background, Clipboard, Color, Length, Point, Rectangle, Shell, Size, Vector, }, iced_style::{ button::StyleSheet as ButtonStyleSheet, container::StyleSheet as ContainerStyleSheet, rule::StyleSheet as RuleStyleSheet, }, iced_widget::container::draw_background, theme, widget::{icon::from_name, Icon}, Apply, }; use keyframe::{ ease, functions::{EaseInOutCubic, EaseOutCubic}, }; use std::{ collections::{HashMap, HashSet, VecDeque}, time::{Duration, Instant}, }; pub struct Tabs<'a, Message, Renderer> where Renderer: cosmic::iced_core::Renderer, Renderer::Theme: RuleStyleSheet, { elements: Vec>, id: Option, height: Length, width: Length, group_focused: bool, scroll_to: Option, } #[derive(Debug, Clone, Copy)] struct ScrollAnimationState { start_time: Instant, start: Offset, end: Offset, } #[derive(Debug, Clone)] struct TabAnimationState { previous_bounds: HashMap, next_bounds: HashMap, start_time: Instant, } /// The local state of [`Tabs`]. #[derive(Debug, Clone)] pub struct State { offset_x: Offset, scroll_animation: Option, scroll_to: Option, last_state: Option>, tab_animations: VecDeque, } impl Scrollable for State { fn snap_to(&mut self, offset: RelativeOffset) { self.offset_x = Offset::Relative(offset.x.clamp(0.0, 1.0)); } fn scroll_to(&mut self, offset: AbsoluteOffset) { let new_offset = Offset::Absolute(offset.x.max(0.0)); self.scroll_animation = Some(ScrollAnimationState { start_time: Instant::now(), start: self.offset_x, end: new_offset, }); self.offset_x = new_offset; } } impl Default for State { fn default() -> Self { State { offset_x: Offset::Absolute(0.), scroll_animation: None, scroll_to: None, last_state: None, tab_animations: VecDeque::new(), } } } #[derive(Debug, Clone, Copy)] enum Offset { Absolute(f32), Relative(f32), } impl Offset { fn absolute(self, viewport: f32, content: f32) -> f32 { match self { Offset::Absolute(absolute) => absolute.min((content - viewport).max(0.0)), Offset::Relative(percentage) => ((content - viewport) * percentage).max(0.0), } } } const SCROLL_ANIMATION_DURATION: Duration = Duration::from_millis(200); const TAB_ANIMATION_DURATION: Duration = Duration::from_millis(150); impl<'a, Message, Renderer> Tabs<'a, Message, Renderer> where Renderer: cosmic::iced_core::Renderer + 'a, Renderer: cosmic::iced_core::text::Renderer, Renderer::Theme: ButtonStyleSheet