mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Fade out the edges of ScrollAreas (#8018)
## Before: <img width="381" height="307" alt="image" src="https://github.com/user-attachments/assets/0528ae2a-44bf-4d9e-89a4-c3f4ab438eb2" /> It is very hard here to realize this is a scrollable area ## After <img width="383" height="310" alt="image" src="https://github.com/user-attachments/assets/9e0ee6de-8b96-4e5c-a505-f57977010990" /> The fade at the bottom tells the user they should try scrolling. You can turn if off with `style.spacing.scroll.fade.enabled`
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
use epaint::Margin;
|
use epaint::{Color32, Direction, Margin, Shape};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
||||||
@@ -1019,13 +1019,17 @@ impl ScrollArea {
|
|||||||
.inner;
|
.inner;
|
||||||
|
|
||||||
let (content_size, state) = prepared.end(ui);
|
let (content_size, state) = prepared.end(ui);
|
||||||
ScrollAreaOutput {
|
let output = ScrollAreaOutput {
|
||||||
inner,
|
inner,
|
||||||
id,
|
id,
|
||||||
state,
|
state,
|
||||||
content_size,
|
content_size,
|
||||||
inner_rect,
|
inner_rect,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
paint_fade_areas(ui, &output);
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1504,3 +1508,89 @@ impl Prepared {
|
|||||||
(content_size, state)
|
(content_size, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Paint fade-out gradients at the top and/or bottom of a scroll area to
|
||||||
|
/// indicate that more content is available beyond the visible region.
|
||||||
|
fn paint_fade_areas<R>(ui: &Ui, scroll_output: &ScrollAreaOutput<R>) {
|
||||||
|
let crate::style::ScrollFadeStyle {
|
||||||
|
enabled,
|
||||||
|
size: fade_size,
|
||||||
|
} = ui.spacing().scroll.fade;
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bg = ui.stack().bg_color();
|
||||||
|
|
||||||
|
let offset = scroll_output.state.offset;
|
||||||
|
let overflow = scroll_output.content_size - scroll_output.inner_rect.size();
|
||||||
|
|
||||||
|
let paint_rect = scroll_output
|
||||||
|
.inner_rect
|
||||||
|
.intersect(ui.min_rect())
|
||||||
|
.expand(ui.visuals().clip_rect_margin);
|
||||||
|
|
||||||
|
// Top fade: animate opacity based on how far we've scrolled down.
|
||||||
|
if 0.0 < offset.y {
|
||||||
|
let t = (offset.y / fade_size).clamp(0.0, 1.0);
|
||||||
|
let bg_faded = bg.gamma_multiply(t);
|
||||||
|
let rect = Rect::from_min_max(
|
||||||
|
paint_rect.left_top(),
|
||||||
|
pos2(paint_rect.right(), paint_rect.top() + fade_size),
|
||||||
|
);
|
||||||
|
ui.painter().add(Shape::gradient_rect(
|
||||||
|
rect,
|
||||||
|
Direction::TopDown,
|
||||||
|
[bg_faded, Color32::TRANSPARENT],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom fade: animate opacity based on distance from the bottom.
|
||||||
|
let distance_from_bottom = overflow.y - offset.y;
|
||||||
|
if 0.0 < distance_from_bottom {
|
||||||
|
let t = (distance_from_bottom / fade_size).clamp(0.0, 1.0);
|
||||||
|
let bg_faded = bg.gamma_multiply(t);
|
||||||
|
let rect = Rect::from_min_max(
|
||||||
|
pos2(paint_rect.left(), paint_rect.bottom() - fade_size),
|
||||||
|
paint_rect.right_bottom(),
|
||||||
|
);
|
||||||
|
ui.painter().add(Shape::gradient_rect(
|
||||||
|
rect,
|
||||||
|
Direction::BottomUp,
|
||||||
|
[bg_faded, Color32::TRANSPARENT],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left fade: animate opacity based on how far we've scrolled right.
|
||||||
|
|
||||||
|
if 0.0 < offset.x {
|
||||||
|
let t = (offset.x / fade_size).clamp(0.0, 1.0);
|
||||||
|
let bg_faded = bg.gamma_multiply(t);
|
||||||
|
let rect = Rect::from_min_max(
|
||||||
|
paint_rect.left_top(),
|
||||||
|
pos2(paint_rect.left() + fade_size, paint_rect.bottom()),
|
||||||
|
);
|
||||||
|
ui.painter().add(Shape::gradient_rect(
|
||||||
|
rect,
|
||||||
|
Direction::LeftToRight,
|
||||||
|
[bg_faded, Color32::TRANSPARENT],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right fade: animate opacity based on distance from the right edge.
|
||||||
|
let distance_from_right = overflow.x - offset.x;
|
||||||
|
if 0.0 < distance_from_right {
|
||||||
|
let t = (distance_from_right / fade_size).clamp(0.0, 1.0);
|
||||||
|
let bg_faded = bg.gamma_multiply(t);
|
||||||
|
let rect = Rect::from_min_max(
|
||||||
|
pos2(paint_rect.right() - fade_size, paint_rect.top()),
|
||||||
|
paint_rect.right_bottom(),
|
||||||
|
);
|
||||||
|
ui.painter().add(Shape::gradient_rect(
|
||||||
|
rect,
|
||||||
|
Direction::RightToLeft,
|
||||||
|
[bg_faded, Color32::TRANSPARENT],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Align,
|
Align, Direction,
|
||||||
emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2},
|
emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2},
|
||||||
};
|
};
|
||||||
const INFINITY: f32 = f32::INFINITY;
|
const INFINITY: f32 = f32::INFINITY;
|
||||||
@@ -87,36 +87,6 @@ impl Region {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Layout direction, one of [`LeftToRight`](Direction::LeftToRight), [`RightToLeft`](Direction::RightToLeft), [`TopDown`](Direction::TopDown), [`BottomUp`](Direction::BottomUp).
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub enum Direction {
|
|
||||||
LeftToRight,
|
|
||||||
RightToLeft,
|
|
||||||
TopDown,
|
|
||||||
BottomUp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_horizontal(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::LeftToRight | Self::RightToLeft => true,
|
|
||||||
Self::TopDown | Self::BottomUp => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_vertical(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::LeftToRight | Self::RightToLeft => false,
|
|
||||||
Self::TopDown | Self::BottomUp => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered".
|
/// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered".
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ pub use emath::{
|
|||||||
remap_clamp, vec2,
|
remap_clamp, vec2,
|
||||||
};
|
};
|
||||||
pub use epaint::{
|
pub use epaint::{
|
||||||
ClippedPrimitive, ColorImage, CornerRadius, ImageData, Margin, Mesh, PaintCallback,
|
ClippedPrimitive, ColorImage, CornerRadius, Direction, ImageData, Margin, Mesh, PaintCallback,
|
||||||
PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex,
|
PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex,
|
||||||
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
||||||
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
|
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
|
||||||
|
|||||||
@@ -586,6 +586,8 @@ pub struct ScrollStyle {
|
|||||||
/// This is only for floating scroll bars.
|
/// This is only for floating scroll bars.
|
||||||
/// Solid scroll bars are always opaque.
|
/// Solid scroll bars are always opaque.
|
||||||
pub interact_handle_opacity: f32,
|
pub interact_handle_opacity: f32,
|
||||||
|
|
||||||
|
pub fade: ScrollFadeStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScrollStyle {
|
impl Default for ScrollStyle {
|
||||||
@@ -616,6 +618,8 @@ impl ScrollStyle {
|
|||||||
dormant_handle_opacity: 0.0,
|
dormant_handle_opacity: 0.0,
|
||||||
active_handle_opacity: 0.6,
|
active_handle_opacity: 0.6,
|
||||||
interact_handle_opacity: 1.0,
|
interact_handle_opacity: 1.0,
|
||||||
|
|
||||||
|
fade: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,6 +703,8 @@ impl ScrollStyle {
|
|||||||
dormant_handle_opacity,
|
dormant_handle_opacity,
|
||||||
active_handle_opacity,
|
active_handle_opacity,
|
||||||
interact_handle_opacity,
|
interact_handle_opacity,
|
||||||
|
|
||||||
|
fade,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -772,6 +778,49 @@ impl ScrollStyle {
|
|||||||
ui.label("Inner margin");
|
ui.label("Inner margin");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
fade.ui(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Controls if and how to fade out the sides of a [`crate::ScrollArea`]
|
||||||
|
/// to indicate there is more there if you scroll.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
|
pub struct ScrollFadeStyle {
|
||||||
|
/// Show fade areas?
|
||||||
|
pub enabled: bool,
|
||||||
|
|
||||||
|
/// Size of the fade-area (height for vertical scrolling,
|
||||||
|
/// width for horizontal scrolling).
|
||||||
|
pub size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScrollFadeStyle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
size: 20.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScrollFadeStyle {
|
||||||
|
pub fn ui(&mut self, ui: &mut Ui) {
|
||||||
|
let Self { enabled, size } = self;
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.checkbox(enabled, "Fade edges");
|
||||||
|
});
|
||||||
|
|
||||||
|
if *enabled {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(DragValue::new(size).range(0.0..=64.0));
|
||||||
|
ui.label("Fade size");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ impl UiStack {
|
|||||||
!self.info.frame.stroke.is_empty()
|
!self.info.frame.stroke.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The background color of this [`Ui`].
|
/// The background color of this [`crate::Ui`].
|
||||||
///
|
///
|
||||||
/// This blend together all [`Frame::fill`] colors
|
/// This blend together all [`Frame::fill`] colors
|
||||||
/// up to the root.
|
/// up to the root.
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b9ad01a55950f96a3ae9e48a2c026143d11ffee62bff4f83b4529cd884ce11f0
|
oid sha256:65776e4d9e31e7163117166915a0cfaaece98b1dd69695533655dee889ac5597
|
||||||
size 169683
|
size 169277
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl crate::Demo for StripDemo {
|
|||||||
impl crate::View for StripDemo {
|
impl crate::View for StripDemo {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let dark_mode = ui.visuals().dark_mode;
|
let dark_mode = ui.visuals().dark_mode;
|
||||||
let faded_color = ui.visuals().window_fill();
|
let faded_color = ui.stack().bg_color();
|
||||||
let faded_color = |color: Color32| -> Color32 {
|
let faded_color = |color: Color32| -> Color32 {
|
||||||
use egui::Rgba;
|
use egui::Rgba;
|
||||||
let t = if dark_mode { 0.95 } else { 0.8 };
|
let t = if dark_mode { 0.95 } else { 0.8 };
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8c630df841e98043132b920338793745549116890c1bc52d8d10b0def896a1e6
|
oid sha256:583003c59f40515a5de435ee1eea2ee6fceae409c7881456401b004c6409896a
|
||||||
size 114409
|
size 114575
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:627114dcbda4f3d2255d34926ed0b77c679d248ed1d822d723479b1e6652c67a
|
oid sha256:8815473873602211a95e0a077dc77560de154a0a1a2f9c1418d90746670075f7
|
||||||
size 249258
|
size 248630
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b5b965a7c690fd8e8646812513e2417170b687fd37e29d220c29127ba0cc200c
|
oid sha256:2881255fa694b713a3c3049f30143ee60a20b68105fb724fbba188f81d34e572
|
||||||
size 172609
|
size 171778
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:931f38ade8373ff79801c05c5d4397f2c5fcfa27022f2e1abe9eb29d561a3aef
|
oid sha256:63fb9d15956efa69818cfd5398b4dfaa796dfbe1df3fb171b8ae413a6f641c9f
|
||||||
size 76022
|
size 76049
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:89986132c5d2a3ccccf0a16cb2b0be07b7c5512838cc10ec9067022e7a238515
|
oid sha256:4ac837d03e3e8959196942d030a8511d00cdd0d5ac9bfe761d80b59d32e73581
|
||||||
size 63918
|
size 63967
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9c81d5a5915dac60297293b90cd3fc0d188a9335e99b318996e3e2934de7bee1
|
oid sha256:4107ba4569b3e37ce6720595cfc4de60aec11dcfdb835720442b244e121209fc
|
||||||
size 483497
|
size 482862
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:eb8737af84c3d3b0c054b7e2a8bcb04685243d84cb13b72a1372dc40dbfd14fb
|
oid sha256:6f6d516cc3d1439256a19f72153468125bac1647e2e211e841579545570faef7
|
||||||
size 7267
|
size 7366
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f1651bb1b9bbaa3c65ecd07c39c57527f4beb4c607581a5b2596a49dcf4c5db3
|
oid sha256:2e05d28e5541eb5926b4758e611f5c340311269fcd8b63055ff3a6793abbb140
|
||||||
size 7996
|
size 8279
|
||||||
|
|||||||
27
crates/epaint/src/direction.rs
Normal file
27
crates/epaint/src/direction.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/// A cardinal direction, one of [`LeftToRight`](Direction::LeftToRight), [`RightToLeft`](Direction::RightToLeft), [`TopDown`](Direction::TopDown), [`BottomUp`](Direction::BottomUp).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub enum Direction {
|
||||||
|
LeftToRight,
|
||||||
|
RightToLeft,
|
||||||
|
TopDown,
|
||||||
|
BottomUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_horizontal(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::LeftToRight | Self::RightToLeft => true,
|
||||||
|
Self::TopDown | Self::BottomUp => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_vertical(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::LeftToRight | Self::RightToLeft => false,
|
||||||
|
Self::TopDown | Self::BottomUp => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ mod brush;
|
|||||||
pub mod color;
|
pub mod color;
|
||||||
mod corner_radius;
|
mod corner_radius;
|
||||||
mod corner_radius_f32;
|
mod corner_radius_f32;
|
||||||
|
mod direction;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
mod margin;
|
mod margin;
|
||||||
mod margin_f32;
|
mod margin_f32;
|
||||||
@@ -50,6 +51,7 @@ pub use self::{
|
|||||||
color::ColorMode,
|
color::ColorMode,
|
||||||
corner_radius::CornerRadius,
|
corner_radius::CornerRadius,
|
||||||
corner_radius_f32::CornerRadiusF32,
|
corner_radius_f32::CornerRadiusF32,
|
||||||
|
direction::Direction,
|
||||||
image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta},
|
image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta},
|
||||||
margin::Margin,
|
margin::Margin,
|
||||||
margin_f32::*,
|
margin_f32::*,
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ pub struct Vertex {
|
|||||||
pub color: Color32, // 32 bit
|
pub color: Color32, // 32 bit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Vertex {
|
||||||
|
/// An untextured vertex
|
||||||
|
#[inline]
|
||||||
|
pub fn untextured(pos: Pos2, color: Color32) -> Self {
|
||||||
|
Self {
|
||||||
|
pos,
|
||||||
|
uv: WHITE_UV,
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
#[cfg(all(feature = "unity", not(feature = "_override_unity")))]
|
#[cfg(all(feature = "unity", not(feature = "_override_unity")))]
|
||||||
@@ -159,11 +171,7 @@ impl Mesh {
|
|||||||
self.texture_id == TextureId::default(),
|
self.texture_id == TextureId::default(),
|
||||||
"Mesh has an assigned texture"
|
"Mesh has an assigned texture"
|
||||||
);
|
);
|
||||||
self.vertices.push(Vertex {
|
self.vertices.push(Vertex::untextured(pos, color));
|
||||||
pos,
|
|
||||||
uv: WHITE_UV,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a triangle.
|
/// Add a triangle.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};
|
use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
|
Color32, CornerRadius, Direction, Mesh, Stroke, StrokeKind, TextureId, Vertex,
|
||||||
stroke::PathStroke,
|
stroke::PathStroke,
|
||||||
text::{FontId, FontsView, Galley},
|
text::{FontId, FontsView, Galley},
|
||||||
};
|
};
|
||||||
@@ -297,6 +297,32 @@ impl Shape {
|
|||||||
Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
|
Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Paints a gradient rectangle that transitions from `color_from` to `color_to`
|
||||||
|
/// along the given `direction`.
|
||||||
|
///
|
||||||
|
/// For example, [`Direction::TopDown`] paints `color_from` at the top edge fading
|
||||||
|
/// to `color_to` at the bottom edge.
|
||||||
|
#[inline]
|
||||||
|
pub fn gradient_rect(rect: Rect, direction: Direction, [from, to]: [Color32; 2]) -> Self {
|
||||||
|
let (left_top, right_top, left_bottom, right_bottom) = match direction {
|
||||||
|
Direction::TopDown => (from, from, to, to),
|
||||||
|
Direction::BottomUp => (to, to, from, from),
|
||||||
|
Direction::LeftToRight => (from, to, from, to),
|
||||||
|
Direction::RightToLeft => (to, from, to, from),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::from(Mesh {
|
||||||
|
indices: vec![0, 1, 2, 2, 1, 3],
|
||||||
|
vertices: vec![
|
||||||
|
Vertex::untextured(rect.left_top(), left_top),
|
||||||
|
Vertex::untextured(rect.right_top(), right_top),
|
||||||
|
Vertex::untextured(rect.left_bottom(), left_bottom),
|
||||||
|
Vertex::untextured(rect.right_bottom(), right_bottom),
|
||||||
|
],
|
||||||
|
texture_id: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(clippy::needless_pass_by_value)]
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
pub fn text(
|
pub fn text(
|
||||||
fonts: &mut FontsView<'_>,
|
fonts: &mut FontsView<'_>,
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap,
|
|||||||
use crate::{
|
use crate::{
|
||||||
CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
|
CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
|
||||||
EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
|
EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
|
||||||
StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, color::ColorMode, emath,
|
StrokeKind, TextShape, TextureId, Vertex, color::ColorMode, emath, stroke::PathStroke,
|
||||||
stroke::PathStroke, texture_atlas::PreparedDisc,
|
texture_atlas::PreparedDisc,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -809,11 +809,8 @@ fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32
|
|||||||
} else {
|
} else {
|
||||||
out.reserve_triangles(n as usize);
|
out.reserve_triangles(n as usize);
|
||||||
let idx = out.vertices.len() as u32;
|
let idx = out.vertices.len() as u32;
|
||||||
out.vertices.extend(path.iter().map(|p| Vertex {
|
out.vertices
|
||||||
pos: p.pos,
|
.extend(path.iter().map(|p| Vertex::untextured(p.pos, fill_color)));
|
||||||
uv: WHITE_UV,
|
|
||||||
color: fill_color,
|
|
||||||
}));
|
|
||||||
for i in 2..n {
|
for i in 2..n {
|
||||||
out.add_triangle(idx, idx + i - 1, idx + i);
|
out.add_triangle(idx, idx + i - 1, idx + i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:81620caad6d420f3bd0f224e5b07a02960a42436208a98d3aa012e5db61a743a
|
oid sha256:5dfd9b576e0ab4b47a0f8c8acfc2664f92faa88cc7fb088409bff359fa1dfadd
|
||||||
size 1510
|
size 1861
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f915eafb6490ff456c5b0a7c74c38ef143262bdf74a0c6561b9cf6ee66a679ea
|
oid sha256:4529530bb46af68260ca910a91f888e8b296790be2b976b450cec884799f53b4
|
||||||
size 1501
|
size 1953
|
||||||
|
|||||||
Reference in New Issue
Block a user