Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
bash committed Jul 8, 2024
1 parent f9476e0 commit 6c4ed36
Show file tree
Hide file tree
Showing 9 changed files with 719 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ pub use {
layers::{LayerId, Order},
layout::*,
load::SizeHint,
memory::{Memory, Options},
memory::{Memory, Options, ThemePreference},
painter::Painter,
response::{InnerResponse, Response},
sense::Sense,
Expand Down
8 changes: 8 additions & 0 deletions crates/egui/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,14 @@ impl Areas {
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ThemePreference {
Dark,
Light,
System,
}

// ----------------------------------------------------------------------------

#[test]
Expand Down
17 changes: 14 additions & 3 deletions crates/egui/src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! * `ui.add(Label::new("Text").text_color(color::red));`
//! * `if ui.add(Button::new("Click me")).clicked() { … }`
use theme_switch::ThemeSwitch;

use crate::*;

mod button;
Expand All @@ -21,6 +23,7 @@ mod separator;
mod slider;
mod spinner;
pub mod text_edit;
mod theme_switch;

pub use self::{
button::Button,
Expand Down Expand Up @@ -134,9 +137,17 @@ pub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) {

/// Show a small button to switch to/from dark/light mode (globally).
pub fn global_dark_light_mode_switch(ui: &mut Ui) {
let style: crate::Style = (*ui.ctx().style()).clone();
let new_visuals = style.visuals.light_dark_small_toggle_button(ui);
if let Some(visuals) = new_visuals {
let mut theme = if ui.ctx().style().visuals.dark_mode {
ThemePreference::Dark
} else {
ThemePreference::Light
};
let response = ui.add(ThemeSwitch::new(&mut theme).show_follow_system(false));
if response.changed {
let visuals = match theme {
ThemePreference::Dark | ThemePreference::System => Visuals::dark(),
ThemePreference::Light => Visuals::light(),
};
ui.ctx().set_visuals(visuals);
}
}
Expand Down
106 changes: 106 additions & 0 deletions crates/egui/src/widgets/theme_switch/arc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::{vec2, Color32, Painter, Pos2, Shape, Stroke, Vec2};
use epaint::CubicBezierShape;
use std::f32::consts::FRAC_PI_2;
use std::ops::RangeInclusive;

#[derive(Debug, Clone, PartialEq)]
pub struct ArcShape {
center: Pos2,
radius: f32,
range: RangeInclusive<f32>,
fill: Color32,
stroke: Stroke,
}

impl ArcShape {
pub fn new(
center: impl Into<Pos2>,
radius: impl Into<f32>,
range: impl Into<RangeInclusive<f32>>,
fill: impl Into<Color32>,
stroke: impl Into<Stroke>,
) -> Self {
Self {
center: center.into(),
radius: radius.into(),
range: range.into(),
fill: fill.into(),
stroke: stroke.into(),
}
}

pub fn approximate_as_beziers(&self) -> impl Iterator<Item = CubicBezierShape> + Clone {
let fill = self.fill;
let stroke = self.stroke;
approximate_with_beziers(self.center, self.radius, self.range.clone())
.map(move |p| CubicBezierShape::from_points_stroke(p, false, fill, stroke))
}

pub fn paint(&self, painter: &Painter) {
painter.extend(self.approximate_as_beziers().map(Shape::from));
}
}

// Implementation based on:
// Riškus, Aleksas. (2006). Approximation of a cubic bezier curve by circular arcs and vice versa.
// Information Technology and Control. 35.

fn approximate_with_beziers(
center: Pos2,
radius: f32,
range: RangeInclusive<f32>,
) -> impl Iterator<Item = [Pos2; 4]> + Clone {
QuarterTurnsIter(Some(range))
.map(move |r| approximate_with_bezier(center, radius, *r.start(), *r.end()))
}

fn approximate_with_bezier(center: Pos2, radius: f32, start: f32, end: f32) -> [Pos2; 4] {
let p1 = center + radius * Vec2::angled(start);
let p4 = center + radius * Vec2::angled(end);

let a = p1 - center;
let b = p4 - center;
let q1 = a.length_sq();
let q2 = q1 + a.dot(b);
let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2) / (a.x * b.y - a.y * b.x);

let p2 = center + vec2(a.x - k2 * a.y, a.y + k2 * a.x);
let p3 = center + vec2(b.x + k2 * b.y, b.y - k2 * b.x);

[p1, p2, p3, p4]
}

// We can approximate at most one quadrant of the circle
// so we divide it up into individual chunks that we then approximate
// using bezier curves.
#[derive(Clone)]
struct QuarterTurnsIter(Option<RangeInclusive<f32>>);

const QUARTER_TURN: f32 = FRAC_PI_2;
impl Iterator for QuarterTurnsIter {
type Item = RangeInclusive<f32>;

fn next(&mut self) -> Option<Self::Item> {
let (start, end) = self.0.clone()?.into_inner();
let distance = end - start;
if distance < QUARTER_TURN {
self.0 = None;
Some(start..=end)
} else {
let new_start = start + (QUARTER_TURN * distance.signum());
self.0 = Some(new_start..=end);
Some(start..=new_start)
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
if let Some((start, end)) = self.0.clone().map(|x| x.into_inner()) {
let turns = (start - end).abs() / QUARTER_TURN;
let lower = turns.floor() as usize;
let upper = turns.ceil() as usize;
(lower, Some(upper))
} else {
(0, None)
}
}
}
38 changes: 38 additions & 0 deletions crates/egui/src/widgets/theme_switch/cogwheel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use super::painter_ext::PainterExt;
use crate::Painter;
use emath::{vec2, Pos2, Rect, Rot2};
use epaint::{Color32, RectShape, Rounding};
use std::f32::consts::TAU;

pub(crate) fn cogwheel(painter: &Painter, center: Pos2, radius: f32, color: Color32) {
let inner_radius = 0.5 * radius;
let outer_radius = 0.8 * radius;
let thickness = 0.3 * radius;

painter.circle(
center,
inner_radius + thickness / 2.,
Color32::TRANSPARENT,
(thickness, color),
);

let cogs = 8;
let cog_width = radius / 3.;
let cog_rounding = radius / 16.;
let cog_length = radius - outer_radius + thickness / 2.;

for n in 0..cogs {
let cog_center = center - vec2(0., outer_radius + cog_length / 2. - thickness / 2.);
let cog_size = vec2(cog_width, cog_length);
let cog = RectShape::filled(
Rect::from_center_size(cog_center, cog_size),
Rounding {
nw: cog_rounding,
ne: cog_rounding,
..Default::default()
},
color,
);
painter.add_rotated(cog, Rot2::from_angle(TAU / cogs as f32 * n as f32), center);
}
}
Loading

0 comments on commit 6c4ed36

Please sign in to comment.