Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/aspectron/egui-notify
Browse files Browse the repository at this point in the history
  • Loading branch information
surinder83singh committed Aug 23, 2024
1 parent 1a7153f commit da020bf
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 79 deletions.
13 changes: 10 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egui-notify"
version = "0.9.0"
version = "0.15.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/ItsEthra/egui-notify"
Expand All @@ -16,8 +16,15 @@ path = "src/lib.rs"
egui = { path="../egui/crates/egui", default-features = false }

[dev-dependencies]
eframe = { version = "0.22", default-features = false, features = [
eframe = { version = "0.28.0", default-features = false, features = [
"default_fonts",
"glow",
] }
egui-phosphor = "0.2.0"
egui-phosphor = { git = "https://github.com/ItsEthra/egui-phosphor", branch = "main" }

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
all = { level = "deny", priority = 0 }
enum_glob_use = { level = "deny", priority = 2 }
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 ItsEthra
Copyright (c) 2022-2023 ItsEthra

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
# egui-notify

Simple notifications library for [`egui`](https://github.com/emilk/egui)

![example_image](media/toasts_type.png)

![example_video](media/toasts_example_video.gif)
# Usage

## Usage

```rust
use egui_notify::Toasts;
use std::time::Duration;

// initialize once
let mut toasts = Toasts::default();
```

```rust
// somewhere within [egui::App::update]...
toasts.info("Hello world!").set_duration(Duration::from_secs(5));
// ...
toasts.show(ctx);
```

# Installation
## Installation

```sh
cargo add egui-notify
```

```toml
[dependencies]
egui-notify = "0.8"
egui-notify = "0.15.0"
```

# Difference to [`egui-toast`](https://github.com/urholaukkarinen/egui-toast)
### `egui-notify` has
- Animations for appearing/disappearing toasts
- Duration meter for expiring toasts
- Toast positioning not influenced by which `Context` you pass to it (like if for example, you passed in a `Context` already altered for an `egui::Window`)
- Differing methodology (create `Toasts` instance once, save save somewhere in application state)
- Threadsafe `Toasts` instance, implements `Send`, `Sync`.
- No support for custom toasts
## Difference to [`egui-toast`](https://github.com/urholaukkarinen/egui-toast)

### `egui-notify` has

- Animations for appearing/disappearing toasts
- Duration meter for expiring toasts
- Toast positioning not influenced by which `Context` you pass to it (like if for example, you passed in a `Context` already altered for an `egui::Window`)
- Differing methodology (create `Toasts` instance once, save save somewhere in application state)
- Threadsafe `Toasts` instance, implements `Send`, `Sync`.
- No support for custom toasts
4 changes: 2 additions & 2 deletions examples/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ fn main() -> eframe::Result<()> {
}
cc.egui_ctx.set_fonts(font_def);

Box::new(ExampleApp {
Ok(Box::new(ExampleApp {
caption: r#"Hello! It's a multiline caption
Next line
Another one
Expand All @@ -157,7 +157,7 @@ And another one"#
font_size: 16.,
custom_level_string: "$".into(),
custom_level_color: egui::Color32::GREEN,
})
}))
}),
)
}
24 changes: 12 additions & 12 deletions src/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,41 @@ pub enum Anchor {

impl Anchor {
#[inline]
pub(crate) fn anim_side(&self) -> f32 {
pub(crate) const fn anim_side(&self) -> f32 {
match self {
Anchor::TopRight | Anchor::BottomRight => 1.,
Anchor::TopLeft | Anchor::BottomLeft => -1.,
Self::TopRight | Self::BottomRight => 1.,
Self::TopLeft | Self::BottomLeft => -1.,
}
}
}

impl Anchor {
pub(crate) fn screen_corner(&self, sc: Pos2, margin: Vec2) -> Pos2 {
let mut out = match self {
Anchor::TopRight => pos2(sc.x, 0.),
Anchor::TopLeft => pos2(0., 0.),
Anchor::BottomRight => sc,
Anchor::BottomLeft => pos2(0., sc.y),
Self::TopRight => pos2(sc.x, 0.),
Self::TopLeft => pos2(0., 0.),
Self::BottomRight => sc,
Self::BottomLeft => pos2(0., sc.y),
};
self.apply_margin(&mut out, margin);
out
}

pub(crate) fn apply_margin(&self, pos: &mut Pos2, margin: Vec2) {
match self {
Anchor::TopRight => {
Self::TopRight => {
pos.x -= margin.x;
pos.y += margin.y;
}
Anchor::TopLeft => {
Self::TopLeft => {
pos.x += margin.x;
pos.y += margin.y
pos.y += margin.y;
}
Anchor::BottomRight => {
Self::BottomRight => {
pos.x -= margin.x;
pos.y -= margin.y;
}
Anchor::BottomLeft => {
Self::BottomLeft => {
pos.x += margin.x;
pos.y -= margin.y;
}
Expand Down
63 changes: 34 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct Toasts {

impl Toasts {
/// Creates new [`Toasts`] instance.
#[must_use]
pub const fn new() -> Self {
Self {
anchor: Anchor::TopRight,
Expand All @@ -69,11 +70,10 @@ impl Toasts {
if self.reverse {
self.toasts.insert(0, toast);
return self.toasts.get_mut(0).unwrap();
} else {
self.toasts.push(toast);
let l = self.toasts.len() - 1;
return self.toasts.get_mut(l).unwrap();
}
self.toasts.push(toast);
let l = self.toasts.len() - 1;
self.toasts.get_mut(l).unwrap()
}

/// Dismisses the oldest toast
Expand All @@ -92,7 +92,7 @@ impl Toasts {

/// Dismisses all toasts
pub fn dismiss_all_toasts(&mut self) {
for toast in self.toasts.iter_mut() {
for toast in &mut self.toasts {
toast.dismiss();
}
}
Expand Down Expand Up @@ -195,13 +195,13 @@ impl Toasts {
toasts.retain(|t| !t.state.disappeared());

// Start disappearing expired toasts
toasts.iter_mut().for_each(|t| {
for t in toasts.iter_mut() {
if let Some((_initial_d, current_d)) = t.duration {
if current_d <= 0. {
t.state = ToastState::Disapper
t.state = ToastState::Disappear;
}
}
});
}

// `held` used to prevent sticky removal
if ctx.input(|i| i.pointer.primary_released()) {
Expand All @@ -212,9 +212,16 @@ impl Toasts {
let mut update = false;

for (i, toast) in toasts.iter_mut().enumerate() {
// Decrease duration if idling
let anim_offset = toast.width * (1. - ease_in_cubic(toast.value));
pos.x += anim_offset * anchor.anim_side();
let rect = toast.calc_anchored_rect(pos, *anchor);

if let Some((_, d)) = toast.duration.as_mut() {
if toast.state.idling() {
// Check if we hover over the toast and if true don't decrease the duration
let hover_pos = ctx.input(|i| i.pointer.hover_pos());
let is_outside_rect = hover_pos.map_or(true, |pos| !rect.contains(pos));

if is_outside_rect && toast.state.idling() {
*d -= ctx.input(|i| i.stable_dt);
update = true;
}
Expand Down Expand Up @@ -265,11 +272,10 @@ impl Toasts {
ToastLevel::None => None,
};

let (action_width, action_height) = if let Some(icon_galley) = icon_galley.as_ref() {
(icon_galley.rect.width(), icon_galley.rect.height())
} else {
(0., 0.)
};
let (action_width, action_height) =
icon_galley.as_ref().map_or((0., 0.), |icon_galley| {
(icon_galley.rect.width(), icon_galley.rect.height())
});

// Create closing cross
let cross_galley = if toast.closable {
Expand All @@ -287,11 +293,10 @@ impl Toasts {
None
};

let (cross_width, cross_height) = if let Some(cross_galley) = cross_galley.as_ref() {
(cross_galley.rect.width(), cross_galley.rect.height())
} else {
(0., 0.)
};
let (cross_width, cross_height) =
cross_galley.as_ref().map_or((0., 0.), |cross_galley| {
(cross_galley.rect.width(), cross_galley.rect.height())
});

let icon_x_padding = (0., padding.x);
let cross_x_padding = (padding.x, 0.);
Expand All @@ -307,12 +312,12 @@ impl Toasts {
cross_width + cross_x_padding.0 + cross_x_padding.1
};

toast.width = icon_width_padded + caption_width + cross_width_padded + (padding.x * 2.);
toast.height = action_height.max(caption_height).max(cross_height) + padding.y * 2.;

let anim_offset = toast.width * (1. - ease_in_cubic(toast.value));
pos.x += anim_offset * anchor.anim_side();
let rect = toast.calc_anchored_rect(pos, *anchor);
toast.width = padding
.x
.mul_add(2., icon_width_padded + caption_width + cross_width_padded);
toast.height = padding
.y
.mul_add(2., action_height.max(caption_height).max(cross_height));

// Required due to positioning of the next toast
pos.x -= anim_offset * anchor.anim_side();
Expand All @@ -326,7 +331,7 @@ impl Toasts {
{
let oy = toast.height / 2. - action_height / 2.;
let ox = padding.x + icon_x_padding.0;
p.galley(rect.min + vec2(ox, oy), icon_galley);
p.galley(rect.min + vec2(ox, oy), icon_galley, Color32::BLACK);
}

// Paint caption
Expand All @@ -342,15 +347,15 @@ impl Toasts {
cross_width + cross_x_padding.0
};
let ox = (toast.width / 2. - caption_width / 2.) + o_from_icon / 2. - o_from_cross / 2.;
p.galley(rect.min + vec2(ox, oy), caption_galley);
p.galley(rect.min + vec2(ox, oy), caption_galley, Color32::BLACK);

// Paint cross
if let Some(cross_galley) = cross_galley {
let cross_rect = cross_galley.rect;
let oy = toast.height / 2. - cross_height / 2.;
let ox = toast.width - cross_width - cross_x_padding.1 - padding.x;
let cross_pos = rect.min + vec2(ox, oy);
p.galley(cross_pos, cross_galley);
p.galley(cross_pos, cross_galley, Color32::BLACK);

let screen_cross = Rect {
max: cross_pos + cross_rect.max.to_vec2(),
Expand Down
Loading

0 comments on commit da020bf

Please sign in to comment.