// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A clickable [`Controller`] widget.
use crate::widget::Controller;
use crate::{Data, Env, Event, EventCtx, LifeCycle, LifeCycleCtx, MouseButton, Widget};
use tracing::{instrument, trace};
/// A clickable [`Controller`] widget. Pass this and a child widget to a
/// [`ControllerHost`] to make the child interactive. More conveniently, this is
/// available as an [`on_click`] method via [`WidgetExt`].
///
/// This is an alternative to the standard [`Button`] widget, for when you want
/// to make an arbitrary widget clickable.
///
/// The child widget will also be updated on [`LifeCycle::HotChanged`] and
/// mouse down, which can be useful for painting based on `ctx.is_active()`
/// and `ctx.is_hot()`.
///
/// [`ControllerHost`]: super::ControllerHost
/// [`on_click`]: super::WidgetExt::on_click
/// [`WidgetExt`]: super::WidgetExt
/// [`Button`]: super::Button
pub struct Click<T> {
/// A closure that will be invoked when the child widget is clicked.
action: Box<dyn Fn(&mut EventCtx, &mut T, &Env)>,
}
impl<T: Data> Click<T> {
/// Create a new clickable [`Controller`] widget.
pub fn new(action: impl Fn(&mut EventCtx, &mut T, &Env) + 'static) -> Self {
Click {
action: Box::new(action),
}
}
}
impl<T: Data, W: Widget<T>> Controller<T, W> for Click<T> {
#[instrument(
name = "Click",
level = "trace",
skip(self, child, ctx, event, data, env)
)]
fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
match event {
Event::MouseDown(mouse_event) => {
if mouse_event.button == MouseButton::Left && !ctx.is_disabled() {
ctx.set_active(true);
ctx.request_paint();
trace!("Widget {:?} pressed", ctx.widget_id());
}
}
Event::MouseUp(mouse_event) => {
if ctx.is_active() && mouse_event.button == MouseButton::Left {
ctx.set_active(false);
if ctx.is_hot() && !ctx.is_disabled() {
(self.action)(ctx, data, env);
}
ctx.request_paint();
trace!("Widget {:?} released", ctx.widget_id());
}
}
_ => {}
}
child.event(ctx, event, data, env);
}
#[instrument(
name = "Click",
level = "trace",
skip(self, child, ctx, event, data, env)
)]
fn lifecycle(
&mut self,
child: &mut W,
ctx: &mut LifeCycleCtx,
event: &LifeCycle,
data: &T,
env: &Env,
) {
if let LifeCycle::HotChanged(_) | LifeCycle::FocusChanged(_) = event {
ctx.request_paint();
}
child.lifecycle(ctx, event, data, env);
}
}