diff --git a/debian/copyright b/debian/copyright index 69f3ae2..9f51475 100644 --- a/debian/copyright +++ b/debian/copyright @@ -62,6 +62,27 @@ Comment: sibling `Sora-LICENSE.txt`. Upstream: https://github.com/sora-xor/sora-font +Files: themes/default/cursors/* +Copyright: 2002-2014 The GNOME Project and the Adwaita icon theme authors +License: CC-BY-SA-3.0 or LGPL-3, and CC-BY-SA-4.0 +Comment: + Pointer cursors under themes/default/cursors/ are GNOME's *Adwaita* + cursor theme — the cursors GNOME Shell ships — bundled verbatim from + the adwaita-icon-theme distribution so the default ltk theme is + self-contained (it does not depend on adwaita-icon-theme being + installed). The files are in XCursor binary format, one per CSS cursor + name (default, text, pointer, *-resize, …) plus the customary X11 alias + symlinks (arrow → default, hand2 → pointer, …); the sibling + cursor.theme makes the tree a valid XCursor theme. They are unmodified + from upstream. A Wayland compositor draws them in response to + wp_cursor_shape_v1, so ltk applications get them without bundling their + own. Upstream offers the artwork under the disjunction in the License + field; redistributing under any one of the options satisfies the + licence. Attribution shorthand: "Cursors by the GNOME Project". The + full functional and licence notes live in + themes/default/cursors/README.md and themes/default/cursors/LICENSE.md. + Upstream: https://gitlab.gnome.org/GNOME/adwaita-icon-theme + License: LGPL-2.1-only This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -196,3 +217,40 @@ License: CC-BY-4.0 The licence text above is a summary; the canonical legal text at the URL is what governs use, distribution and modification of the Licensed Material referenced under `Files: themes/default/icons/catalogue/*`. + +License: CC-BY-SA-3.0 + Creative Commons Attribution-ShareAlike 3.0 (CC BY-SA 3.0). + . + You are free to share and adapt the material for any purpose, even + commercially, provided you give appropriate credit, provide a link to + the licence, indicate if changes were made, and distribute your + contributions under the same licence as the original (ShareAlike). + . + The canonical legal text is at: + https://creativecommons.org/licenses/by-sa/3.0/legalcode + . + A human-readable summary (which is not a substitute for the licence) is + available at: + https://creativecommons.org/licenses/by-sa/3.0/ + . + The summary above governs the Adwaita cursor files referenced under + `Files: themes/default/cursors/*` when distributed under the 3.0 option. + +License: CC-BY-SA-4.0 + Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). + . + You are free to share and adapt the material for any purpose, even + commercially, under the same Attribution and ShareAlike terms as the + CC BY-SA 3.0 paragraph above. + . + The canonical legal text is at: + https://creativecommons.org/licenses/by-sa/4.0/legalcode + . + A human-readable summary (which is not a substitute for the licence) is + available at: + https://creativecommons.org/licenses/by-sa/4.0/ + +License: LGPL-3 + The Adwaita cursors are alternatively available under the GNU Lesser + General Public License version 3. On Debian systems the complete text + of the LGPL version 3 is in `/usr/share/common-licenses/LGPL-3`. diff --git a/src/draw/layout.rs b/src/draw/layout.rs index 12360d8..bd7deaa 100644 --- a/src/draw/layout.rs +++ b/src/draw/layout.rs @@ -195,12 +195,18 @@ pub( crate ) fn layout_and_draw( { canvas.stroke_rect( rect, color, width, c.corners ); } + let vp = canvas.viewport_logical(); + let em = crate::types::Length::EM_BASE_DEFAULT; + let pad_l = c.pad_left.resolve( vp, em ); + let pad_r = c.pad_right.resolve( vp, em ); + let pad_t = c.pad_top.resolve( vp, em ); + let pad_b = c.pad_bottom.resolve( vp, em ); let inner = crate::types::Rect { - x: rect.x + c.pad_left, - y: rect.y + c.pad_top, - width: ( rect.width - c.pad_left - c.pad_right ).max( 0.0 ), - height: ( rect.height - c.pad_top - c.pad_bottom ).max( 0.0 ), + x: rect.x + pad_l, + y: rect.y + pad_t, + width: ( rect.width - pad_l - pad_r ).max( 0.0 ), + height: ( rect.height - pad_t - pad_b ).max( 0.0 ), }; let result = layout_and_draw::( c.child.as_ref(), canvas, inner, ctx, flat_idx ); diff --git a/src/input/gesture/mod.rs b/src/input/gesture/mod.rs index 9a63ced..eda48c6 100644 --- a/src/input/gesture/mod.rs +++ b/src/input/gesture/mod.rs @@ -68,6 +68,10 @@ pub struct GestureState /// Index of the Scroll viewport that owns the current gesture, if /// the press landed inside one. pub scrolling_widget: Option<( usize, crate::widget::scroll::ScrollAxis )>, + /// Scroll viewports containing the press, innermost first. + pub scroll_candidates: Vec<( usize, crate::widget::scroll::ScrollAxis )>, + /// `true` once the first 8 px of motion has pinned `scrolling_widget` to a definite axis. + pub scroll_locked: bool, /// Scroll-viewport drag exceeded the 8 px start tolerance — the /// release will be consumed as a scroll instead of a tap. pub scroll_drag_started: bool, @@ -136,6 +140,8 @@ impl GestureState start: None, pressed_idx: None, scrolling_widget: None, + scroll_candidates: Vec::new(), + scroll_locked: false, scroll_drag_started: false, horizontal_drag_started: false, vertical_drag_started: false, @@ -175,9 +181,12 @@ impl GestureState }).unwrap_or( ( None, None ) ); self.start = Some( pos ); - self.scrolling_widget = scroll_rects.iter().rev() - .find( |( r, _, _ )| r.contains( pos ) ) - .map( |( _, idx, ax )| ( *idx, *ax ) ); + self.scroll_candidates = scroll_rects.iter() + .filter( |( r, _, _ )| r.contains( pos ) ) + .map( |( _, idx, ax )| ( *idx, *ax ) ) + .collect(); + self.scrolling_widget = self.scroll_candidates.first().copied(); + self.scroll_locked = false; self.scroll_drag_started = false; self.horizontal_drag_started = false; self.vertical_drag_started = false; @@ -289,19 +298,30 @@ impl GestureState return MoveOutcome::Idle; } - // Scroll viewport drag: mutate offset in place and advance the - // gesture origin so the next delta is frame-to-frame, not - // press-to-now (otherwise the first 8 px trip the threshold - // and the entire scroll gets absorbed into one delta). Both - // axes are routed independently; an axis the viewport does not - // allow is just ignored (delta still consumed by the gesture, - // so the swipe handler does not fight the scroll for it). - if let Some( ( scroll_idx, axis ) ) = self.scrolling_widget + if self.scrolling_widget.is_some() { if let Some( start ) = self.start { - let dx = pos.x - start.x; - let dy = pos.y - start.y; + let dx = pos.x - start.x; + let dy = pos.y - start.y; + + if !self.scroll_locked + { + if dx.abs() <= 8.0 && dy.abs() <= 8.0 + { + return MoveOutcome::Idle; + } + let prefer_x = dx.abs() > dy.abs(); + if let Some( c ) = self.scroll_candidates.iter() + .find( |( _, ax )| if prefer_x { ax.allows_x() } else { ax.allows_y() } ) + .copied() + { + self.scrolling_widget = Some( c ); + } + self.scroll_locked = true; + } + + let ( scroll_idx, axis ) = self.scrolling_widget.unwrap(); let entry = scroll_offsets.entry( scroll_idx ).or_insert( ( 0.0, 0.0 ) ); if axis.allows_x() { entry.0 = ( entry.0 - dx ).max( 0.0 ); } if axis.allows_y() { entry.1 = ( entry.1 - dy ).max( 0.0 ); } diff --git a/src/input/pointer/scroll.rs b/src/input/pointer/scroll.rs index c482f3b..0a439d2 100644 --- a/src/input/pointer/scroll.rs +++ b/src/input/pointer/scroll.rs @@ -33,7 +33,7 @@ impl AppData let scroll_hit = { let ss = self.surface( focus ); - ss.scroll_rects.iter().rev() + ss.scroll_rects.iter() .find( |( r, _, _ )| r.contains( pos ) ) .map( |( _, idx, ax )| ( *idx, *ax ) ) }; diff --git a/src/layout/column.rs b/src/layout/column.rs index aec5bf2..7cb29dd 100644 --- a/src/layout/column.rs +++ b/src/layout/column.rs @@ -224,21 +224,19 @@ impl Column let pad = self.resolved_padding( canvas ); let spacing = self.resolved_spacing( canvas ); - // Flexible spacers and Scroll widgets claim remaining vertical space. - // Fixed-height spacers behave like normal fixed-size children. let total_weight: u32 = self.children.iter() .map( |c| match c { Element::Spacer( s ) if s.resolved_height( canvas ).is_none() => s.weight, - Element::Scroll( _ ) => 1, - _ => 0, + Element::Scroll( s ) if s.axis.allows_y() => 1, + _ => 0, } ) .sum(); let fixed_h: f32 = self.children.iter() .map( |c| { - if matches!( c, Element::Scroll( _ ) ) + if matches!( c, Element::Scroll( s ) if s.axis.allows_y() ) { 0.0 } else if let Element::Spacer( s ) = c { @@ -282,7 +280,7 @@ impl Column }; ( inner_w, h ) }, - Element::Scroll( _ ) => + Element::Scroll( s ) if s.axis.allows_y() => { let h = if total_weight > 0 { diff --git a/src/layout/wrap_grid.rs b/src/layout/wrap_grid.rs index ea6e41e..242b0db 100644 --- a/src/layout/wrap_grid.rs +++ b/src/layout/wrap_grid.rs @@ -2,7 +2,7 @@ // Copyright (C) 2026 Liberux Labs, S. L. use crate::render::Canvas; -use crate::types::Rect; +use crate::types::{ Length, Rect }; use crate::widget::Element; /// A grid layout that wraps children into rows of a fixed column count. @@ -35,12 +35,12 @@ pub struct WrapGrid pub children: Vec>, /// Number of columns per row. pub columns: usize, - /// Horizontal gap between cells (pixels). - pub spacing_x: f32, - /// Vertical gap between rows (pixels). - pub spacing_y: f32, - /// Padding on all sides (pixels). - pub padding: f32, + /// Horizontal gap between cells. + pub spacing_x: Length, + /// Vertical gap between rows. + pub spacing_y: Length, + /// Padding on all sides. + pub padding: Length, /// When `true`, a partial last row is centred horizontally within /// the grid's content rect instead of being left-aligned. pub centre_last_row: bool, @@ -56,31 +56,32 @@ impl WrapGrid } /// Set both horizontal and vertical gap between cells (default 8.0). - pub fn spacing( mut self, s: f32 ) -> Self + pub fn spacing( mut self, s: impl Into ) -> Self { + let s = s.into(); self.spacing_x = s; self.spacing_y = s; self } /// Set only the horizontal gap between cells; leaves vertical spacing untouched. - pub fn spacing_x( mut self, s: f32 ) -> Self + pub fn spacing_x( mut self, s: impl Into ) -> Self { - self.spacing_x = s; + self.spacing_x = s.into(); self } /// Set only the vertical gap between rows; leaves horizontal spacing untouched. - pub fn spacing_y( mut self, s: f32 ) -> Self + pub fn spacing_y( mut self, s: impl Into ) -> Self { - self.spacing_y = s; + self.spacing_y = s.into(); self } /// Set the padding on all sides (default 0.0). - pub fn padding( mut self, p: f32 ) -> Self + pub fn padding( mut self, p: impl Into ) -> Self { - self.padding = p; + self.padding = p.into(); self } @@ -92,6 +93,17 @@ impl WrapGrid self } + fn resolved( &self, canvas: &Canvas ) -> ( f32, f32, f32 ) + { + let vp = canvas.viewport_logical(); + let em = Length::EM_BASE_DEFAULT; + ( + self.spacing_x.resolve( vp, em ), + self.spacing_y.resolve( vp, em ), + self.padding.resolve( vp, em ), + ) + } + /// Compute the preferred size given an available width. pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32) { @@ -99,12 +111,13 @@ impl WrapGrid { return ( max_width, 0.0 ); } + let ( sx, sy, pad ) = self.resolved( canvas ); let cols = self.columns; - let inner_w = (max_width - self.padding * 2.0).max( 0.0 ); - let cell_w = (inner_w - self.spacing_x * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32; + let inner_w = (max_width - pad * 2.0).max( 0.0 ); + let cell_w = (inner_w - sx * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32; let row_count = (self.children.len() + cols - 1) / cols; - let mut total_h = self.padding * 2.0; + let mut total_h = pad * 2.0; for row in 0..row_count { let start = row * cols; @@ -114,7 +127,7 @@ impl WrapGrid .map( |c| c.preferred_size( cell_w, canvas ).1 ) .fold( 0.0_f32, f32::max ); total_h += row_h; - if row + 1 < row_count { total_h += self.spacing_y; } + if row + 1 < row_count { total_h += sy; } } ( max_width, total_h ) } @@ -126,11 +139,12 @@ impl WrapGrid { return Vec::new(); } + let ( sx, sy, pad ) = self.resolved( canvas ); let cols = self.columns; - let inner_w = (rect.width - self.padding * 2.0).max( 0.0 ); - let cell_w = (inner_w - self.spacing_x * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32; - let x0 = rect.x + self.padding; - let mut y = rect.y + self.padding; + let inner_w = (rect.width - pad * 2.0).max( 0.0 ); + let cell_w = (inner_w - sx * (cols as f32 - 1.0)).max( 0.0 ) / cols as f32; + let x0 = rect.x + pad; + let mut y = rect.y + pad; let row_count = (self.children.len() + cols - 1) / cols; let mut out = Vec::with_capacity( self.children.len() ); @@ -148,16 +162,16 @@ impl WrapGrid let row_offset = if self.centre_last_row && items_in_row < cols { let missing = (cols - items_in_row) as f32; - missing * (cell_w + self.spacing_x) / 2.0 + missing * (cell_w + sx) / 2.0 } else { 0.0 }; for col in 0..items_in_row { - let x = x0 + row_offset + col as f32 * (cell_w + self.spacing_x); + let x = x0 + row_offset + col as f32 * (cell_w + sx); let crect = Rect { x, y, width: cell_w, height: row_h }; out.push( ( crect, start + col ) ); } - y += row_h + self.spacing_y; + y += row_h + sy; } out } @@ -396,9 +410,9 @@ pub fn grid( columns: usize ) -> WrapGrid { children: Vec::new(), columns, - spacing_x: 8.0, - spacing_y: 8.0, - padding: 0.0, + spacing_x: Length::px( 8.0 ), + spacing_y: Length::px( 8.0 ), + padding: Length::px( 0.0 ), centre_last_row: false, } } diff --git a/src/lib.rs b/src/lib.rs index 5b1b2c5..9cb7c0f 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,6 +159,16 @@ //! - **Sora Regular** (`src/theme/fallback/Sora-Regular.otf`) — the //! embedded font fallback, [SIL OFL 1.1](https://scripts.sil.org/OFL), //! © The Sora Project Authors, Jonathan Barnbrook, Julián Moncada. +//! - **Pointer cursors** under `themes/default/cursors/` — GNOME's +//! *Adwaita* cursor theme (the cursors GNOME Shell ships), bundled +//! verbatim, © the GNOME Project. Offered upstream under +//! [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) *or* +//! [LGPL 3](https://www.gnu.org/licenses/lgpl-3.0.html), and +//! [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) for +//! the newer assets; any one option satisfies the licence. See +//! `themes/default/cursors/README.md` (what the set is and how it is +//! used) and `themes/default/cursors/LICENSE.md` (attribution). +//! Upstream: . //! //! The remaining artwork in the default theme (wallpapers, lockscreens, //! launcher logo, brand-mark variants, per-application icons) is @@ -230,6 +240,7 @@ pub use gles_render::{ BorrowedGlesTexture, GlesVersion }; pub use render::is_software_render; pub use wallpaper::{ WallpaperBundle, ImageData }; pub use types::{ Color, Corners, CursorShape, Length, LengthBase, Point, Rect, Size, WidgetId }; +pub use types::{ design_reference, set_design_reference }; pub use widget::{ Element, button, icon_button, text_edit, image as img_widget, text, container }; pub use widget::button::ButtonVariant; pub use widget::slider::{ Slider, slider, SliderAxis }; diff --git a/src/types.rs b/src/types.rs index 823fa94..bd5a09b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -24,6 +24,8 @@ //! `ltk::Rect`, …) so application code rarely needs the `ltk::types::` //! prefix. +use std::sync::atomic::{ AtomicU32, Ordering }; + /// An RGBA color with floating-point channels in the range `[0.0, 1.0]`. #[ derive( Debug, Clone, Copy, PartialEq ) ] pub struct Color @@ -524,6 +526,17 @@ impl Length pub const fn vmax( v: f32 ) -> Self { Self::from_base( LengthBase::Vmax( v ) ) } pub const fn em( v: f32 ) -> Self { Self::from_base( LengthBase::Em( v ) ) } + /// "Design pixel": `px` interpreted at the reference vmin set via + /// [`set_design_reference`] (defaults to 412 px — the eydos mobile + /// reference width). The result is a `Vmin` value clamped to + /// `[px * 0.7, px * 1.5]`, so the layout scales with the screen + /// without collapsing on tiny surfaces or ballooning on 4K. + pub fn dp( px: f32 ) -> Self + { + let r = design_reference(); + Length::vmin( px / r * 100.0 ).clamp( px * 0.7, px * 1.5 ) + } + /// Resolve to a concrete logical-pixel value given a viewport and an /// `em_base` (the root font size that `Em` is a fraction of). pub fn resolve( &self, viewport: ( f32, f32 ), em_base: f32 ) -> f32 @@ -573,6 +586,23 @@ impl Length } } +static DESIGN_REFERENCE_BITS: AtomicU32 = AtomicU32::new( 412.0_f32.to_bits() ); + +/// Set the reference vmin width that [`Length::dp`] interprets `px` against. +/// Call once at startup (e.g. before [`crate::run`]) to align the design +/// scale to the surface mock-up the app was designed for. +pub fn set_design_reference( reference_vmin: f32 ) +{ + DESIGN_REFERENCE_BITS.store( reference_vmin.to_bits(), Ordering::Relaxed ); +} + +/// Current value used by [`Length::dp`] — the px width at which `dp(n)` +/// resolves to `n` logical pixels. +pub fn design_reference() -> f32 +{ + f32::from_bits( DESIGN_REFERENCE_BITS.load( Ordering::Relaxed ) ) +} + impl From for Length { fn from( v: f32 ) -> Self { Length::px( v ) } diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs index 0ebd754..cd1d840 100644 --- a/src/widget/container/mod.rs +++ b/src/widget/container/mod.rs @@ -2,7 +2,7 @@ // Copyright (C) 2026 Liberux Labs, S. L. use crate::theme::Paint; -use crate::types::{ Color, Corners }; +use crate::types::{ Color, Corners, Length }; use crate::render::Canvas; use super::Element; @@ -72,15 +72,16 @@ pub struct Container /// left edge, …) without hitting the renderer with an offset /// trick. pub corners: Corners, - /// Padding on the top edge in logical px — gap between the - /// container's top boundary and its child. - pub pad_top: f32, - /// Padding on the right edge in logical px. - pub pad_right: f32, - /// Padding on the bottom edge in logical px. - pub pad_bottom: f32, - /// Padding on the left edge in logical px. - pub pad_left: f32, + /// Padding on the top edge — gap between the container's top boundary + /// and its child. Stored as a [`Length`] so it can scale with the + /// viewport via [`Length::dp`] / [`Length::vmin`]. + pub pad_top: Length, + /// Padding on the right edge. + pub pad_right: Length, + /// Padding on the bottom edge. + pub pad_bottom: Length, + /// Padding on the left edge. + pub pad_left: Length, pub opacity: f32, /// Optional `( color, width_px )` border stroke painted around the /// container's rounded rectangle, after the fill / surface and @@ -109,10 +110,10 @@ impl Container background: None, surface: None, corners: Corners::ZERO, - pad_top: 0.0, - pad_right: 0.0, - pad_bottom: 0.0, - pad_left: 0.0, + pad_top: Length::px( 0.0 ), + pad_right: Length::px( 0.0 ), + pad_bottom: Length::px( 0.0 ), + pad_left: Length::px( 0.0 ), opacity: 1.0, border: None, max_width: None, @@ -197,8 +198,9 @@ impl Container /// edges, so calling this first and then a per-edge setter is the /// idiomatic way to express "uniform padding except for one /// edge". - pub fn padding( mut self, p: f32 ) -> Self + pub fn padding( mut self, p: impl Into ) -> Self { + let p = p.into(); self.pad_top = p; self.pad_right = p; self.pad_bottom = p; @@ -207,16 +209,18 @@ impl Container } /// Set horizontal padding (left + right each). - pub fn padding_h( mut self, p: f32 ) -> Self + pub fn padding_h( mut self, p: impl Into ) -> Self { + let p = p.into(); self.pad_left = p; self.pad_right = p; self } /// Set vertical padding (top + bottom each). - pub fn padding_v( mut self, p: f32 ) -> Self + pub fn padding_v( mut self, p: impl Into ) -> Self { + let p = p.into(); self.pad_top = p; self.pad_bottom = p; self @@ -225,30 +229,30 @@ impl Container /// Set the top edge padding only. Pairs with /// [`padding_bottom`](Self::padding_bottom) for asymmetric /// vertical insets. - pub fn padding_top( mut self, p: f32 ) -> Self + pub fn padding_top( mut self, p: impl Into ) -> Self { - self.pad_top = p; + self.pad_top = p.into(); self } /// Set the right edge padding only. - pub fn padding_right( mut self, p: f32 ) -> Self + pub fn padding_right( mut self, p: impl Into ) -> Self { - self.pad_right = p; + self.pad_right = p.into(); self } /// Set the bottom edge padding only. - pub fn padding_bottom( mut self, p: f32 ) -> Self + pub fn padding_bottom( mut self, p: impl Into ) -> Self { - self.pad_bottom = p; + self.pad_bottom = p.into(); self } /// Set the left edge padding only. - pub fn padding_left( mut self, p: f32 ) -> Self + pub fn padding_left( mut self, p: impl Into ) -> Self { - self.pad_left = p; + self.pad_left = p.into(); self } @@ -271,9 +275,15 @@ impl Container /// Return the preferred `(width, height)` accounting for padding. pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> ( f32, f32 ) { + let vp = canvas.viewport_logical(); + let em = Length::EM_BASE_DEFAULT; + let pad_l = self.pad_left.resolve( vp, em ); + let pad_r = self.pad_right.resolve( vp, em ); + let pad_t = self.pad_top.resolve( vp, em ); + let pad_b = self.pad_bottom.resolve( vp, em ); let avail = self.max_width.map( |m| max_width.min( m ) ).unwrap_or( max_width ); - let pad_x = self.pad_left + self.pad_right; - let pad_y = self.pad_top + self.pad_bottom; + let pad_x = pad_l + pad_r; + let pad_y = pad_t + pad_b; let inner_w = ( avail - pad_x ).max( 0.0 ); let ( cw, ch ) = self.child.preferred_size( inner_w, canvas ); ( cw + pad_x, ch + pad_y ) diff --git a/src/widget/container/tests.rs b/src/widget/container/tests.rs index ac2882d..bbc47b8 100644 --- a/src/widget/container/tests.rs +++ b/src/widget/container/tests.rs @@ -3,6 +3,7 @@ use super::*; use crate::layout::spacer::spacer; +use crate::types::Length; #[ test ] fn default_no_background() @@ -15,30 +16,30 @@ fn default_no_background() fn padding_sets_all_four_sides() { let c = container::<()>( spacer() ).padding( 10.0 ); - assert_eq!( c.pad_top, 10.0 ); - assert_eq!( c.pad_right, 10.0 ); - assert_eq!( c.pad_bottom, 10.0 ); - assert_eq!( c.pad_left, 10.0 ); + assert_eq!( c.pad_top, Length::px( 10.0 ) ); + assert_eq!( c.pad_right, Length::px( 10.0 ) ); + assert_eq!( c.pad_bottom, Length::px( 10.0 ) ); + assert_eq!( c.pad_left, Length::px( 10.0 ) ); } #[ test ] fn padding_h_only_touches_left_and_right() { let c = container::<()>( spacer() ).padding_h( 8.0 ); - assert_eq!( c.pad_left, 8.0 ); - assert_eq!( c.pad_right, 8.0 ); - assert_eq!( c.pad_top, 0.0 ); - assert_eq!( c.pad_bottom, 0.0 ); + assert_eq!( c.pad_left, Length::px( 8.0 ) ); + assert_eq!( c.pad_right, Length::px( 8.0 ) ); + assert_eq!( c.pad_top, Length::px( 0.0 ) ); + assert_eq!( c.pad_bottom, Length::px( 0.0 ) ); } #[ test ] fn padding_v_only_touches_top_and_bottom() { let c = container::<()>( spacer() ).padding_v( 6.0 ); - assert_eq!( c.pad_top, 6.0 ); - assert_eq!( c.pad_bottom, 6.0 ); - assert_eq!( c.pad_left, 0.0 ); - assert_eq!( c.pad_right, 0.0 ); + assert_eq!( c.pad_top, Length::px( 6.0 ) ); + assert_eq!( c.pad_bottom, Length::px( 6.0 ) ); + assert_eq!( c.pad_left, Length::px( 0.0 ) ); + assert_eq!( c.pad_right, Length::px( 0.0 ) ); } #[ test ] @@ -48,10 +49,10 @@ fn per_edge_overrides_uniform_padding() let c = container::<()>( spacer() ) .padding( 12.0 ) .padding_bottom( 22.0 ); - assert_eq!( c.pad_top, 12.0 ); - assert_eq!( c.pad_right, 12.0 ); - assert_eq!( c.pad_bottom, 22.0 ); - assert_eq!( c.pad_left, 12.0 ); + assert_eq!( c.pad_top, Length::px( 12.0 ) ); + assert_eq!( c.pad_right, Length::px( 12.0 ) ); + assert_eq!( c.pad_bottom, Length::px( 22.0 ) ); + assert_eq!( c.pad_left, Length::px( 12.0 ) ); } #[ test ] diff --git a/src/widget/scroll/mod.rs b/src/widget/scroll/mod.rs index 98a84dc..335c492 100644 --- a/src/widget/scroll/mod.rs +++ b/src/widget/scroll/mod.rs @@ -93,13 +93,27 @@ impl Scroll self } - /// Returns `(max_width, 0.0)` — the Scroll node claims all remaining space in - /// the parent layout, exactly like a [`Spacer`](crate::layout::spacer::Spacer). - /// The actual viewport size is determined at render time from the rect the - /// parent assigns. - pub fn preferred_size( &self, max_width: f32, _canvas: &Canvas ) -> (f32, f32) + /// Preferred size — axis-aware. + /// + /// - **Vertical or both**: returns `(max_width, 0.0)`. The Scroll node + /// claims all remaining space in the parent layout, exactly like a + /// [`Spacer`](crate::layout::spacer::Spacer). The actual viewport size + /// is determined at render time from the rect the parent assigns. + /// - **Horizontal-only**: claims `max_width` but reports the child's + /// natural height. A horizontal scroll has an intrinsic height (the + /// row of items it clips) and must not steal Y space from its siblings + /// when it sits inside a [`Column`](crate::layout::column::Column). + pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32) { - ( max_width, 0.0 ) + match self.axis + { + ScrollAxis::Horizontal => + { + let ( _, h ) = self.child.preferred_size( max_width, canvas ); + ( max_width, h ) + } + ScrollAxis::Vertical | ScrollAxis::Both => ( max_width, 0.0 ), + } } /// No-op — rendering is handled entirely by `layout_and_draw` in `draw.rs`. diff --git a/tests/cursor_assets.rs b/tests/cursor_assets.rs new file mode 100644 index 0000000..81a05c0 --- /dev/null +++ b/tests/cursor_assets.rs @@ -0,0 +1,105 @@ +use std::io::Read; +use std::path::{ Path, PathBuf }; + +/// Every shape `ltk::CursorShape` can request, by its CSS / freedesktop +/// cursor name. The default theme must ship an XCursor file (or alias +/// symlink) for each so the compositor never has to fall back. Keep in +/// sync with the `CursorShape` enum in `src/types.rs`. +const CURSOR_NAMES: &[ &str ] = +&[ + "default", "context-menu", "help", "pointer", "progress", "wait", + "cell", "crosshair", "text", "vertical-text", "alias", "copy", "move", + "no-drop", "not-allowed", "grab", "grabbing", "e-resize", "n-resize", + "ne-resize", "nw-resize", "s-resize", "se-resize", "sw-resize", + "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", + "col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out", +]; + +fn theme_dir() -> PathBuf +{ + PathBuf::from( env!( "CARGO_MANIFEST_DIR" ) ).join( "themes/default" ) +} + +fn cursors_dir() -> PathBuf +{ + theme_dir().join( "cursors" ) +} + +/// True if `path` (following symlinks) is a binary XCursor file, i.e. it +/// opens and starts with the four magic bytes `Xcur`. +fn is_xcursor( path: &Path ) -> bool +{ + let mut file = match std::fs::File::open( path ) + { + Ok( f ) => f, + Err( _ ) => return false, + }; + let mut magic = [ 0u8; 4 ]; + file.read_exact( &mut magic ).is_ok() && &magic == b"Xcur" +} + +#[ test ] +fn cursor_theme_manifest_present() +{ + let manifest = theme_dir().join( "cursor.theme" ); + let body = std::fs::read_to_string( &manifest ) + .unwrap_or_else( |e| panic!( "reading {}: {e}", manifest.display() ) ); + assert! + ( + body.contains( "[Icon Theme]" ), + "cursor.theme is missing the [Icon Theme] header", + ); +} + +#[ test ] +fn every_cursor_shape_has_a_valid_xcursor_file() +{ + let dir = cursors_dir(); + let missing: Vec<&str> = CURSOR_NAMES + .iter() + .copied() + .filter( |name| !is_xcursor( &dir.join( name ) ) ) + .collect(); + + assert! + ( + missing.is_empty(), + "default theme has no valid XCursor file for: {missing:?}", + ); +} + +/// Packaging (`dh_install`) can in principle drop or break the alias +/// symlinks; a dangling link would make a cursor name unresolvable at +/// runtime. Every entry must resolve to an existing file. +#[ test ] +fn no_dangling_entries() +{ + let dir = cursors_dir(); + let entries = std::fs::read_dir( &dir ) + .unwrap_or_else( |e| panic!( "reading {}: {e}", dir.display() ) ); + + let mut dangling = Vec::new(); + for entry in entries + { + let path = entry.expect( "dir entry" ).path(); + // `metadata` follows symlinks, so a broken link is an Err here. + if std::fs::metadata( &path ).is_err() + { + dangling.push( path ); + } + } + + assert!( dangling.is_empty(), "dangling cursor entries: {dangling:?}" ); +} + +/// Guard against the name list above drifting from the enum it mirrors. +#[ test ] +fn cursor_name_list_matches_shape_count() +{ + assert_eq! + ( + CURSOR_NAMES.len(), + 34, + "CURSOR_NAMES is out of sync with ltk::CursorShape (34 variants)", + ); +} diff --git a/themes/default/cursor.theme b/themes/default/cursor.theme new file mode 100644 index 0000000..afdbfc5 --- /dev/null +++ b/themes/default/cursor.theme @@ -0,0 +1,3 @@ +[Icon Theme] +Name=Default +Comment=Liberux default cursor theme diff --git a/themes/default/cursors/LICENSE.md b/themes/default/cursors/LICENSE.md new file mode 100644 index 0000000..8180686 --- /dev/null +++ b/themes/default/cursors/LICENSE.md @@ -0,0 +1,57 @@ +# Cursor theme licence + +The pointer cursors in this directory are GNOME's **Adwaita** cursor +theme — the cursors GNOME Shell ships — bundled verbatim into the ltk +default theme. They are distributed by upstream under a choice of +licences: + +- **Author:** the GNOME Project (Adwaita icon theme authors, 2002–2014) +- **Source:** +- **Licence:** **CC BY-SA 3.0 _or_ LGPL 3**, and CC BY-SA 4.0 for the + newer assets — see below. + +The licence is a disjunction: redistributing the cursors under **any +one** of the offered options satisfies it. ltk passes the choice +through unchanged. + +- CC BY-SA 3.0 — +- CC BY-SA 4.0 — +- LGPL 3 — + +## Required attribution + +When redistributing this directory (or a binary that embeds these +cursor files), credit must be given to the GNOME Project and a link to +the chosen licence must be reachable. The wording is flexible — any of +the following satisfies the obligation: + +> Cursors by the [GNOME Project](https://www.gnome.org), Adwaita cursor +> theme, CC BY-SA 3.0 / LGPL 3. + +> Pointer cursors: © the GNOME Project, Adwaita, distributed under +> CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/). + +Upstream notes that, for attribution, "using 'GNOME Project' is enough". + +The full Debian-style attribution lives in the top-level +[`debian/copyright`](../../../debian/copyright) of the `ltk` source +tree under the `Files: themes/default/cursors/*` paragraph. + +## Modifications + +The cursor files in this directory are **unmodified** copies of the +upstream Adwaita XCursor files (the alias symlinks are part of the +upstream theme). No content changes have been made, so there is nothing +to declare under the ShareAlike "indicate if changes were made" clause. +Anyone needing a pristine copy can also fetch it from the upstream +`adwaita-icon-theme` release. + +## How licensing applies to the rest of the theme + +The rest of the default theme is licensed separately: branding artwork, +app icons and the unknown-app fallback are original Liberux Labs work +under `LGPL-2.1-only`, and the catalogue glyphs under +`themes/default/icons/catalogue/` are Streamline's under CC BY 4.0 (see +that directory's `LICENSE.md`). Forks should preserve the split: +re-license your own artwork freely, but the contents of this `cursors/` +directory remain the GNOME Project's under the terms above. diff --git a/themes/default/cursors/README.md b/themes/default/cursors/README.md new file mode 100644 index 0000000..a44960d --- /dev/null +++ b/themes/default/cursors/README.md @@ -0,0 +1,73 @@ +# Default theme cursors + +This directory holds the pointer cursors for the ltk **default** theme. +They are GNOME's **Adwaita** cursors (what GNOME Shell uses), bundled +verbatim so the theme is self-contained and does not depend on +`adwaita-icon-theme` being installed on the target system. + +Licence and attribution are documented separately in +[`LICENSE.md`](./LICENSE.md). + +## Layout + +Standard XCursor theme layout: + +``` +themes/default/ +├── cursor.theme # XCursor theme manifest (Name=Default) +└── cursors/ + ├── default # one binary XCursor file per CSS cursor name + ├── text + ├── pointer + ├── ew-resize + ├── … + ├── arrow -> default # customary X11 alias symlinks + └── hand2 -> pointer +``` + +- Each regular file is a binary XCursor image set (magic `Xcur`), + carrying one or more nominal sizes and, for the busy cursors, several + animation frames. +- The file **name is the CSS / freedesktop cursor name** (`default`, + `text`, `pointer`, `not-allowed`, `ns-resize`, …), which is exactly + the name a `wp_cursor_shape_v1` shape resolves to. The alias symlinks + (`arrow`, `hand2`, `bottom_left_corner`, …) are the legacy X11 names; + they are not required by the cursor-shape protocol but are kept so the + tree also works as a plain XCursor theme (e.g. for XWayland). + +## Cursor set + +The theme provides a file for every shape ltk can request — the 34 +variants of [`ltk::CursorShape`](../../../src/types.rs), which mirror +`cursor_icon::CursorIcon` 1:1: + +`default`, `context-menu`, `help`, `pointer`, `progress`, `wait`, +`cell`, `crosshair`, `text`, `vertical-text`, `alias`, `copy`, `move`, +`no-drop`, `not-allowed`, `grab`, `grabbing`, `e-resize`, `n-resize`, +`ne-resize`, `nw-resize`, `s-resize`, `se-resize`, `sw-resize`, +`w-resize`, `ew-resize`, `ns-resize`, `nesw-resize`, `nwse-resize`, +`col-resize`, `row-resize`, `all-scroll`, `zoom-in`, `zoom-out`. + +`wait` and `progress` are animated (multiple frames with per-frame +delays). The `tests/cursor_assets.rs` integration test enforces that +every name above resolves to a valid XCursor file. + +## How they are used + +ltk does **not** rasterise cursors itself; as a Wayland client it +declares a shape per widget through `wp_cursor_shape_v1` and the +compositor draws it. The Liberux compositor (forge) resolves the +requested shape against the active theme's `cursors/` directory by CSS +name, picks the image whose nominal size is closest to `24px × output +scale`, and uploads it at the cursor's hotspot — so these files are what +the user actually sees under forge. A compositor that does not advertise +`wp_cursor_shape_v1`, or that uses a different cursor theme, will ignore +this directory. + +## Forking / overriding + +To ship different cursors in a derived theme, drop replacement XCursor +files (same CSS names) into the fork's `cursors/` directory. Keep +`cursor.theme` so the tree stays a valid XCursor theme, and update the +`Files: themes/default/cursors/*` paragraph in `debian/copyright` plus +this directory's `LICENSE.md` to match the new source and licence. diff --git a/themes/default/cursors/X_cursor b/themes/default/cursors/X_cursor new file mode 100644 index 0000000..f5cb308 Binary files /dev/null and b/themes/default/cursors/X_cursor differ diff --git a/themes/default/cursors/alias b/themes/default/cursors/alias new file mode 100644 index 0000000..f233f9e Binary files /dev/null and b/themes/default/cursors/alias differ diff --git a/themes/default/cursors/all-resize b/themes/default/cursors/all-resize new file mode 100644 index 0000000..808bd11 Binary files /dev/null and b/themes/default/cursors/all-resize differ diff --git a/themes/default/cursors/all-scroll b/themes/default/cursors/all-scroll new file mode 100644 index 0000000..e6417de Binary files /dev/null and b/themes/default/cursors/all-scroll differ diff --git a/themes/default/cursors/arrow b/themes/default/cursors/arrow new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/themes/default/cursors/arrow @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/themes/default/cursors/bd_double_arrow b/themes/default/cursors/bd_double_arrow new file mode 120000 index 0000000..22ec3b4 --- /dev/null +++ b/themes/default/cursors/bd_double_arrow @@ -0,0 +1 @@ +nwse-resize \ No newline at end of file diff --git a/themes/default/cursors/bottom_left_corner b/themes/default/cursors/bottom_left_corner new file mode 120000 index 0000000..9ed1e45 --- /dev/null +++ b/themes/default/cursors/bottom_left_corner @@ -0,0 +1 @@ +sw-resize \ No newline at end of file diff --git a/themes/default/cursors/bottom_right_corner b/themes/default/cursors/bottom_right_corner new file mode 120000 index 0000000..4288822 --- /dev/null +++ b/themes/default/cursors/bottom_right_corner @@ -0,0 +1 @@ +se-resize \ No newline at end of file diff --git a/themes/default/cursors/bottom_side b/themes/default/cursors/bottom_side new file mode 120000 index 0000000..a033fc0 --- /dev/null +++ b/themes/default/cursors/bottom_side @@ -0,0 +1 @@ +s-resize \ No newline at end of file diff --git a/themes/default/cursors/cell b/themes/default/cursors/cell new file mode 100644 index 0000000..1e30d4c Binary files /dev/null and b/themes/default/cursors/cell differ diff --git a/themes/default/cursors/col-resize b/themes/default/cursors/col-resize new file mode 100644 index 0000000..dc00c83 Binary files /dev/null and b/themes/default/cursors/col-resize differ diff --git a/themes/default/cursors/context-menu b/themes/default/cursors/context-menu new file mode 100644 index 0000000..f7e481f Binary files /dev/null and b/themes/default/cursors/context-menu differ diff --git a/themes/default/cursors/copy b/themes/default/cursors/copy new file mode 100644 index 0000000..1bf0ba8 Binary files /dev/null and b/themes/default/cursors/copy differ diff --git a/themes/default/cursors/cross b/themes/default/cursors/cross new file mode 120000 index 0000000..67580a7 --- /dev/null +++ b/themes/default/cursors/cross @@ -0,0 +1 @@ +crosshair \ No newline at end of file diff --git a/themes/default/cursors/cross_reverse b/themes/default/cursors/cross_reverse new file mode 120000 index 0000000..67580a7 --- /dev/null +++ b/themes/default/cursors/cross_reverse @@ -0,0 +1 @@ +crosshair \ No newline at end of file diff --git a/themes/default/cursors/crosshair b/themes/default/cursors/crosshair new file mode 100644 index 0000000..e683bb0 Binary files /dev/null and b/themes/default/cursors/crosshair differ diff --git a/themes/default/cursors/default b/themes/default/cursors/default new file mode 100644 index 0000000..7e8b6a2 Binary files /dev/null and b/themes/default/cursors/default differ diff --git a/themes/default/cursors/diamond_cross b/themes/default/cursors/diamond_cross new file mode 120000 index 0000000..67580a7 --- /dev/null +++ b/themes/default/cursors/diamond_cross @@ -0,0 +1 @@ +crosshair \ No newline at end of file diff --git a/themes/default/cursors/dnd-move b/themes/default/cursors/dnd-move new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/themes/default/cursors/dnd-move @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/themes/default/cursors/e-resize b/themes/default/cursors/e-resize new file mode 100644 index 0000000..ec5dbd7 Binary files /dev/null and b/themes/default/cursors/e-resize differ diff --git a/themes/default/cursors/ew-resize b/themes/default/cursors/ew-resize new file mode 100644 index 0000000..6716c4a Binary files /dev/null and b/themes/default/cursors/ew-resize differ diff --git a/themes/default/cursors/fd_double_arrow b/themes/default/cursors/fd_double_arrow new file mode 120000 index 0000000..4e4476b --- /dev/null +++ b/themes/default/cursors/fd_double_arrow @@ -0,0 +1 @@ +nesw-resize \ No newline at end of file diff --git a/themes/default/cursors/fleur b/themes/default/cursors/fleur new file mode 120000 index 0000000..603c648 --- /dev/null +++ b/themes/default/cursors/fleur @@ -0,0 +1 @@ +all-resize \ No newline at end of file diff --git a/themes/default/cursors/grab b/themes/default/cursors/grab new file mode 100644 index 0000000..87cc243 Binary files /dev/null and b/themes/default/cursors/grab differ diff --git a/themes/default/cursors/grabbing b/themes/default/cursors/grabbing new file mode 100644 index 0000000..a717089 Binary files /dev/null and b/themes/default/cursors/grabbing differ diff --git a/themes/default/cursors/hand1 b/themes/default/cursors/hand1 new file mode 120000 index 0000000..2a71685 --- /dev/null +++ b/themes/default/cursors/hand1 @@ -0,0 +1 @@ +grab \ No newline at end of file diff --git a/themes/default/cursors/hand2 b/themes/default/cursors/hand2 new file mode 120000 index 0000000..5ba3f42 --- /dev/null +++ b/themes/default/cursors/hand2 @@ -0,0 +1 @@ +pointer \ No newline at end of file diff --git a/themes/default/cursors/help b/themes/default/cursors/help new file mode 100644 index 0000000..94d9066 Binary files /dev/null and b/themes/default/cursors/help differ diff --git a/themes/default/cursors/left_ptr b/themes/default/cursors/left_ptr new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/themes/default/cursors/left_ptr @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/themes/default/cursors/left_side b/themes/default/cursors/left_side new file mode 120000 index 0000000..9210f6c --- /dev/null +++ b/themes/default/cursors/left_side @@ -0,0 +1 @@ +w-resize \ No newline at end of file diff --git a/themes/default/cursors/move b/themes/default/cursors/move new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/themes/default/cursors/move @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/themes/default/cursors/n-resize b/themes/default/cursors/n-resize new file mode 100644 index 0000000..b746c01 Binary files /dev/null and b/themes/default/cursors/n-resize differ diff --git a/themes/default/cursors/ne-resize b/themes/default/cursors/ne-resize new file mode 100644 index 0000000..a47ba76 Binary files /dev/null and b/themes/default/cursors/ne-resize differ diff --git a/themes/default/cursors/nesw-resize b/themes/default/cursors/nesw-resize new file mode 100644 index 0000000..feab30c Binary files /dev/null and b/themes/default/cursors/nesw-resize differ diff --git a/themes/default/cursors/no-drop b/themes/default/cursors/no-drop new file mode 100644 index 0000000..856376e Binary files /dev/null and b/themes/default/cursors/no-drop differ diff --git a/themes/default/cursors/not-allowed b/themes/default/cursors/not-allowed new file mode 100644 index 0000000..0999668 Binary files /dev/null and b/themes/default/cursors/not-allowed differ diff --git a/themes/default/cursors/ns-resize b/themes/default/cursors/ns-resize new file mode 100644 index 0000000..5961b6a Binary files /dev/null and b/themes/default/cursors/ns-resize differ diff --git a/themes/default/cursors/nw-resize b/themes/default/cursors/nw-resize new file mode 100644 index 0000000..c7dc023 Binary files /dev/null and b/themes/default/cursors/nw-resize differ diff --git a/themes/default/cursors/nwse-resize b/themes/default/cursors/nwse-resize new file mode 100644 index 0000000..5af372a Binary files /dev/null and b/themes/default/cursors/nwse-resize differ diff --git a/themes/default/cursors/pointer b/themes/default/cursors/pointer new file mode 100644 index 0000000..6c4329f Binary files /dev/null and b/themes/default/cursors/pointer differ diff --git a/themes/default/cursors/progress b/themes/default/cursors/progress new file mode 100644 index 0000000..2904095 Binary files /dev/null and b/themes/default/cursors/progress differ diff --git a/themes/default/cursors/question_arrow b/themes/default/cursors/question_arrow new file mode 120000 index 0000000..4cea3ac --- /dev/null +++ b/themes/default/cursors/question_arrow @@ -0,0 +1 @@ +help \ No newline at end of file diff --git a/themes/default/cursors/right_side b/themes/default/cursors/right_side new file mode 120000 index 0000000..2718bc8 --- /dev/null +++ b/themes/default/cursors/right_side @@ -0,0 +1 @@ +e-resize \ No newline at end of file diff --git a/themes/default/cursors/row-resize b/themes/default/cursors/row-resize new file mode 100644 index 0000000..f58fcdb Binary files /dev/null and b/themes/default/cursors/row-resize differ diff --git a/themes/default/cursors/s-resize b/themes/default/cursors/s-resize new file mode 100644 index 0000000..6a8d655 Binary files /dev/null and b/themes/default/cursors/s-resize differ diff --git a/themes/default/cursors/sb_h_double_arrow b/themes/default/cursors/sb_h_double_arrow new file mode 120000 index 0000000..ba57eb3 --- /dev/null +++ b/themes/default/cursors/sb_h_double_arrow @@ -0,0 +1 @@ +ew-resize \ No newline at end of file diff --git a/themes/default/cursors/sb_v_double_arrow b/themes/default/cursors/sb_v_double_arrow new file mode 120000 index 0000000..f860e9b --- /dev/null +++ b/themes/default/cursors/sb_v_double_arrow @@ -0,0 +1 @@ +ns-resize \ No newline at end of file diff --git a/themes/default/cursors/se-resize b/themes/default/cursors/se-resize new file mode 100644 index 0000000..3815722 Binary files /dev/null and b/themes/default/cursors/se-resize differ diff --git a/themes/default/cursors/sw-resize b/themes/default/cursors/sw-resize new file mode 100644 index 0000000..ffd4d99 Binary files /dev/null and b/themes/default/cursors/sw-resize differ diff --git a/themes/default/cursors/tcross b/themes/default/cursors/tcross new file mode 120000 index 0000000..67580a7 --- /dev/null +++ b/themes/default/cursors/tcross @@ -0,0 +1 @@ +crosshair \ No newline at end of file diff --git a/themes/default/cursors/text b/themes/default/cursors/text new file mode 100644 index 0000000..6c7181c Binary files /dev/null and b/themes/default/cursors/text differ diff --git a/themes/default/cursors/top_left_arrow b/themes/default/cursors/top_left_arrow new file mode 120000 index 0000000..331d858 --- /dev/null +++ b/themes/default/cursors/top_left_arrow @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/themes/default/cursors/top_left_corner b/themes/default/cursors/top_left_corner new file mode 120000 index 0000000..61635a8 --- /dev/null +++ b/themes/default/cursors/top_left_corner @@ -0,0 +1 @@ +nw-resize \ No newline at end of file diff --git a/themes/default/cursors/top_right_corner b/themes/default/cursors/top_right_corner new file mode 120000 index 0000000..f196a6c --- /dev/null +++ b/themes/default/cursors/top_right_corner @@ -0,0 +1 @@ +ne-resize \ No newline at end of file diff --git a/themes/default/cursors/top_side b/themes/default/cursors/top_side new file mode 120000 index 0000000..dc1f459 --- /dev/null +++ b/themes/default/cursors/top_side @@ -0,0 +1 @@ +n-resize \ No newline at end of file diff --git a/themes/default/cursors/vertical-text b/themes/default/cursors/vertical-text new file mode 100644 index 0000000..33b64cc Binary files /dev/null and b/themes/default/cursors/vertical-text differ diff --git a/themes/default/cursors/w-resize b/themes/default/cursors/w-resize new file mode 100644 index 0000000..ffa201d Binary files /dev/null and b/themes/default/cursors/w-resize differ diff --git a/themes/default/cursors/wait b/themes/default/cursors/wait new file mode 100644 index 0000000..e18e8d7 Binary files /dev/null and b/themes/default/cursors/wait differ diff --git a/themes/default/cursors/watch b/themes/default/cursors/watch new file mode 120000 index 0000000..fd80437 --- /dev/null +++ b/themes/default/cursors/watch @@ -0,0 +1 @@ +wait \ No newline at end of file diff --git a/themes/default/cursors/xterm b/themes/default/cursors/xterm new file mode 120000 index 0000000..f3a3485 --- /dev/null +++ b/themes/default/cursors/xterm @@ -0,0 +1 @@ +text \ No newline at end of file diff --git a/themes/default/cursors/zoom-in b/themes/default/cursors/zoom-in new file mode 100644 index 0000000..a9ae3ce Binary files /dev/null and b/themes/default/cursors/zoom-in differ diff --git a/themes/default/cursors/zoom-out b/themes/default/cursors/zoom-out new file mode 100644 index 0000000..815c201 Binary files /dev/null and b/themes/default/cursors/zoom-out differ diff --git a/themes/default/theme.json b/themes/default/theme.json index 68ba4c1..ca5e278 100644 --- a/themes/default/theme.json +++ b/themes/default/theme.json @@ -37,7 +37,18 @@ "glass-elev": "#212121", "ink": "#000000", "cyan-deep": "#0091AD", - "teal-fill-end": "#00687C" + "teal-fill-end": "#00687C", + "green": "#00D344", + "green-deep": "#00B41A", + "yellow": "#FFD400", + "orange": "#FF8156", + "orange-deep": "#BD6403", + "pink": "#EF15FA", + "pink-soft": "#FFD7FF", + "sky-deep": "#1E8FD9", + "error": "#F00013", + "error-soft": "#FF6B6B", + "neutral-tertiary": "#807E88" }, "gradients": { @@ -111,6 +122,21 @@ "accent": { "type": "color", "value": "@cyan", "meta": { "semantic": "palette/accent" } }, "divider": { "type": "color", "value": "@navy/14", "meta": { "semantic": "palette/divider" } }, "icon": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/icon" } }, + "danger": { "type": "color", "value": "@error", "meta": { "semantic": "palette/danger" } }, + "text-tertiary": { "type": "color", "value": "@neutral-tertiary", "meta": { "semantic": "palette/text_tertiary" } }, + "accept": { "type": "color", "value": "@green-deep", "meta": { "semantic": "status/accept" } }, + "chip": { "type": "color", "value": "@off-white", "meta": { "semantic": "palette/chip" } }, + "chip-active": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/chip_active" } }, + "chip-active-fg": { "type": "color", "value": "@white", "meta": { "semantic": "palette/chip_active_fg" } }, + "avatar-1": { "type": "color", "value": "@sky", "meta": { "semantic": "avatar/1" } }, + "avatar-2": { "type": "color", "value": "@green", "meta": { "semantic": "avatar/2" } }, + "avatar-3": { "type": "color", "value": "@orange", "meta": { "semantic": "avatar/3" } }, + "avatar-4": { "type": "color", "value": "@pink", "meta": { "semantic": "avatar/4" } }, + "avatar-5": { "type": "color", "value": "@yellow", "meta": { "semantic": "avatar/5" } }, + "avatar-6": { "type": "color", "value": "@sky-deep", "meta": { "semantic": "avatar/6" } }, + "avatar-7": { "type": "color", "value": "@cyan", "meta": { "semantic": "avatar/7" } }, + "avatar-8": { "type": "color", "value": "@green-deep", "meta": { "semantic": "avatar/8" } }, + "avatar-9": { "type": "color", "value": "@orange-deep", "meta": { "semantic": "avatar/9" } }, "shadows-glass": { "type": "shadows", @@ -225,6 +251,21 @@ "accent": { "type": "color", "value": "@cyan", "meta": { "semantic": "palette/accent" } }, "divider": { "type": "color", "value": "@white/14", "meta": { "semantic": "palette/divider" } }, "icon": { "type": "color", "value": "@white", "meta": { "semantic": "palette/icon" } }, + "danger": { "type": "color", "value": "@error-soft", "meta": { "semantic": "palette/danger" } }, + "text-tertiary": { "type": "color", "value": "@white/80", "meta": { "semantic": "palette/text_tertiary" } }, + "accept": { "type": "color", "value": "@green", "meta": { "semantic": "status/accept" } }, + "chip": { "type": "color", "value": "@white/14", "meta": { "semantic": "palette/chip" } }, + "chip-active": { "type": "color", "value": "@cyan", "meta": { "semantic": "palette/chip_active" } }, + "chip-active-fg": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/chip_active_fg" } }, + "avatar-1": { "type": "color", "value": "@sky", "meta": { "semantic": "avatar/1" } }, + "avatar-2": { "type": "color", "value": "@green", "meta": { "semantic": "avatar/2" } }, + "avatar-3": { "type": "color", "value": "@orange", "meta": { "semantic": "avatar/3" } }, + "avatar-4": { "type": "color", "value": "@pink", "meta": { "semantic": "avatar/4" } }, + "avatar-5": { "type": "color", "value": "@yellow", "meta": { "semantic": "avatar/5" } }, + "avatar-6": { "type": "color", "value": "@sky-deep", "meta": { "semantic": "avatar/6" } }, + "avatar-7": { "type": "color", "value": "@cyan", "meta": { "semantic": "avatar/7" } }, + "avatar-8": { "type": "color", "value": "@green-deep", "meta": { "semantic": "avatar/8" } }, + "avatar-9": { "type": "color", "value": "@orange-deep", "meta": { "semantic": "avatar/9" } }, "shadows-glass": { "type": "shadows",