Added Opacity component

Closes #87
This commit is contained in:
Jonathan Johnson 2023-12-13 14:02:39 -08:00
parent 1ea9938198
commit 353db9dc39
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
8 changed files with 108 additions and 12 deletions

2
Cargo.lock generated
View file

@ -1062,7 +1062,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.1.0"
source = "git+https://github.com/khonsulabs/kludgine#90d2035c22bd92ddc5e7edf6c933886c55749bd3"
source = "git+https://github.com/khonsulabs/kludgine#2fffa618dda72fae415b2d25faf9dfe461662246"
dependencies = [
"ahash",
"alot",

View file

@ -132,6 +132,7 @@ impl Object for Player {
context.draw_texture(
frame,
Rect::new(center - zoomed_size / 2, Size::squared(zoomed_size)),
1.,
);
}

View file

@ -41,7 +41,7 @@ pub mod easings;
use std::cmp::Ordering;
use std::fmt::{Debug, Display};
use std::ops::{ControlFlow, Deref, Div, Mul, Sub};
use std::ops::{ControlFlow, Deref, Div, DivAssign, Mul, MulAssign, Sub};
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
@ -1250,6 +1250,12 @@ impl Mul for ZeroToOne {
}
}
impl MulAssign for ZeroToOne {
fn mul_assign(&mut self, rhs: Self) {
self.0 *= rhs.0;
}
}
impl Div for ZeroToOne {
type Output = Self;
@ -1258,6 +1264,12 @@ impl Div for ZeroToOne {
}
}
impl DivAssign for ZeroToOne {
fn div_assign(&mut self, rhs: Self) {
self.0 /= rhs.0;
}
}
impl Div<f32> for ZeroToOne {
type Output = Self;
@ -1271,6 +1283,29 @@ impl Ranged for ZeroToOne {
const MIN: Self = Self::ZERO;
}
impl RequireInvalidation for ZeroToOne {
fn requires_invalidation(&self) -> bool {
false
}
}
impl TryFrom<Component> for ZeroToOne {
type Error = Component;
fn try_from(value: Component) -> Result<Self, Self::Error> {
match value {
Component::Percent(value) => Ok(value),
other => Err(other),
}
}
}
impl From<ZeroToOne> for Component {
fn from(value: ZeroToOne) -> Self {
Component::Percent(value)
}
}
/// An easing function for customizing animations.
#[derive(Debug, Clone)]
pub enum EasingFunction {

View file

@ -14,11 +14,12 @@ use kludgine::figures::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size,
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine};
use crate::animation::ZeroToOne;
use crate::context::sealed::WindowHandle;
use crate::graphics::Graphics;
use crate::styles::components::{
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
TextSize, WidgetBackground,
Opacity, TextSize, WidgetBackground,
};
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::utils::IgnorePoison;
@ -555,19 +556,31 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
Widget: ManageWidget,
Widget::Managed: MapManagedWidget<GraphicsContext<'child, 'window, 'child, 'gfx, 'pass>>,
{
let opacity = self.get(&Opacity);
widget.manage(self).map(|widget| {
let widget = self.widget.for_other(&widget);
let layout = widget.last_layout().map_or_else(
|| Rect::from(self.gfx.clip_rect().size).into_signed(),
|rect| rect - self.gfx.region().origin,
);
let mut gfx = self.gfx.clipped_to(layout);
gfx.opacity *= opacity;
GraphicsContext {
widget,
gfx: Exclusive::Owned(self.gfx.clipped_to(layout)),
gfx: Exclusive::Owned(gfx),
}
})
}
/// Updates `self` to have `opacity`.
///
/// This setting will be mixed with the current opacity value.
#[must_use]
pub fn with_opacity(mut self, opacity: impl Into<ZeroToOne>) -> Self {
self.gfx.opacity *= opacity.into();
self
}
/// Returns a new graphics context that renders to the `clip` rectangle.
pub fn clipped_to(&mut self, clip: Rect<Px>) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> {
GraphicsContext {

View file

@ -13,6 +13,7 @@ use kludgine::{
cosmic_text, ClipGuard, Color, Drawable, Kludgine, ShaderScalable, ShapeSource, TextureSource,
};
use crate::animation::ZeroToOne;
use crate::styles::FontFamilyList;
/// A 2d graphics context
@ -20,6 +21,7 @@ pub struct Graphics<'clip, 'gfx, 'pass> {
renderer: RenderContext<'clip, 'gfx, 'pass>,
region: Rect<Px>,
font_state: &'clip mut FontState,
pub(crate) opacity: ZeroToOne,
}
enum RenderContext<'clip, 'gfx, 'pass> {
@ -35,6 +37,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
region: renderer.clip_rect().into_signed(),
renderer: RenderContext::Renderer(renderer),
font_state,
opacity: ZeroToOne::ONE,
}
}
@ -112,6 +115,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
region,
font_state: &mut *self.font_state,
opacity: self.opacity,
}
}
@ -173,6 +177,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Unit: Zero + ShaderScalable + figures::ScreenUnit + Copy,
{
let mut shape = shape.into();
shape.opacity = Some(
shape
.opacity
.map_or(*self.opacity, |opacity| opacity * *self.opacity),
);
shape.translation += Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer.draw_shape(shape);
}
@ -184,7 +193,8 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
i32: From<<Unit as IntoSigned>::Signed>,
{
let translate = Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer.draw_texture(texture, destination + translate);
self.renderer
.draw_texture(texture, destination + translate, *self.opacity);
}
/// Draws a shape that was created with texture coordinates, applying the
@ -199,6 +209,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Shape: ShapeSource<Unit, true> + 'shape,
{
let mut shape = shape.into();
shape.opacity = Some(
shape
.opacity
.map_or(*self.opacity, |opacity| opacity * *self.opacity),
);
shape.translation += Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer.draw_textured_shape(shape, texture);
}
@ -219,6 +234,10 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Unit: ScreenUnit,
{
let mut text = text.into();
text.opacity = Some(
text.opacity
.map_or(*self.opacity, |opacity| opacity * *self.opacity),
);
text.translation += Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer.draw_text(text);
}
@ -239,6 +258,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Unit: ScreenUnit,
{
let mut buffer = buffer.into();
buffer.opacity = Some(
buffer
.opacity
.map_or(*self.opacity, |opacity| opacity * *self.opacity),
);
buffer.translation += Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer
.draw_text_buffer(buffer, default_color, origin);
@ -272,6 +296,10 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Unit: ScreenUnit,
{
let mut text = text.into();
text.opacity = Some(
text.opacity
.map_or(*self.opacity, |opacity| opacity * *self.opacity),
);
text.translation += Point::<Unit>::from_px(self.translation(), self.scale());
self.renderer.draw_measured_text(text, origin);
}

View file

@ -6,7 +6,7 @@ use kludgine::shapes::CornerRadii;
use kludgine::Color;
use crate::animation::easings::{EaseInOutQuadradic, EaseInQuadradic, EaseOutQuadradic};
use crate::animation::EasingFunction;
use crate::animation::{EasingFunction, ZeroToOne};
use crate::styles::{Dimension, FocusableWidgets, FontFamilyList, VisualOrder};
/// Defines a set of style components for Gooey.
@ -235,5 +235,8 @@ define_components! {
Heading5FontFamily(FontFamilyList, "heading_font_family_5", @HeadingFontFamily)
/// The [`FontFamilyList`] to apply to h6 headings.
Heading6FontFamily(FontFamilyList, "heading_font_family_6", @HeadingFontFamily)
/// The opaqueness of drawing calls
Opacity(ZeroToOne, "opacity", ZeroToOne::ONE)
}
}

View file

@ -1,6 +1,7 @@
//! Widgets that stack in the Z-direction.
use std::fmt;
use std::time::Duration;
use alot::{LotId, OrderedLots};
use gooey::widget::{RootBehavior, WidgetInstance};
@ -8,6 +9,8 @@ use intentional::Assert;
use kludgine::figures::units::{Px, UPx};
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
use crate::animation::easings::{EaseInQuadradic, EaseOutQuadradic};
use crate::animation::{AnimationTarget, Spawn, ZeroToOne};
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext};
use crate::value::{Dynamic, DynamicGuard, Generation, IntoValue, Value};
use crate::widget::{
@ -47,7 +50,7 @@ impl Layers {
if self
.mounted
.get(index)
.map_or(true, |child| child != widget)
.map_or(true, |child| &child.widget != widget)
{
// These entries do not match. See if we can find the
// new id somewhere else, if so we can swap the entries.
@ -56,7 +59,7 @@ impl Layers {
.iter()
.enumerate()
.skip(index + 1)
.find(|(_, child)| *child == widget)
.find(|(_, child)| &child.widget == widget)
{
self.mounted.swap(index, swap_index);
} else {
@ -81,8 +84,8 @@ impl Widget for Layers {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
self.synchronize_children(&mut context.as_event_context());
for child in &self.mounted {
context.for_other(child).redraw();
for mounted in &self.mounted {
context.for_other(mounted).redraw();
}
}
@ -186,6 +189,7 @@ impl OverlayLayer {
requires_hover: false,
on_dismiss: None,
layout: None,
opacity: Dynamic::default(),
},
}
}
@ -200,7 +204,8 @@ impl Widget for OverlayLayer {
continue;
};
context.for_other(mounted).redraw();
let opacity = child.opacity.get_tracking_refresh(context);
context.for_other(mounted).with_opacity(opacity).redraw();
}
}
@ -611,6 +616,7 @@ impl OverlayBuilder<'_> {
/// Shows this overlay, returning a handle that to the displayed overlay.
#[must_use]
pub fn show(self) -> OverlayHandle {
self.fade_in();
self.overlay.state.map_mut(|state| {
state.new_overlays += 1;
OverlayHandle {
@ -620,11 +626,21 @@ impl OverlayBuilder<'_> {
}
})
}
fn fade_in(&self) {
self.layout
.opacity
.transition_to(ZeroToOne::ONE)
.over(Duration::from_millis(250))
.with_easing(EaseOutQuadradic)
.launch();
}
}
#[derive(Debug, Eq, PartialEq)]
struct OverlayLayout {
widget: WidgetRef,
opacity: Dynamic<ZeroToOne>,
relative_to: Option<WidgetId>,
direction: Direction,
requires_hover: bool,

View file

@ -769,7 +769,7 @@ where
_window: kludgine::app::Window<'_, WindowCommand>,
graphics: &mut kludgine::RenderingGraphics<'_, 'pass>,
) -> bool {
self.contents.render(graphics);
self.contents.render(1., graphics);
!self.should_close
}