use std::any::Any;
use std::sync::RwLock;
use super::{Element, ElementPtr, ViewLimits, ViewStretch, share};
use super::context::{BasicContext, Context};
use crate::support::point::Point;
use crate::support::rect::Rect;
use crate::support::color::Color;
use crate::support::theme::get_theme;
use crate::view::{MouseButton, MouseButtonKind};
pub struct Floating {
content: Option<ElementPtr>,
position: RwLock<Point>,
size: RwLock<Point>,
dragging: RwLock<bool>,
drag_offset: RwLock<Point>,
background_color: Color,
border_color: Color,
corner_radius: f32,
shadow: bool,
draggable: bool,
visible: RwLock<bool>,
}
impl Floating {
pub fn new() -> Self {
let theme = get_theme();
Self {
content: None,
position: RwLock::new(Point::new(100.0, 100.0)),
size: RwLock::new(Point::new(200.0, 150.0)),
dragging: RwLock::new(false),
drag_offset: RwLock::new(Point::zero()),
background_color: theme.element_background_color,
border_color: theme.frame_color,
corner_radius: 8.0,
shadow: true,
draggable: true,
visible: RwLock::new(true),
}
}
pub fn content<E: Element + 'static>(mut self, content: E) -> Self {
self.content = Some(share(content));
self
}
pub fn position(self, x: f32, y: f32) -> Self {
*self.position.write().unwrap() = Point::new(x, y);
self
}
pub fn size(self, width: f32, height: f32) -> Self {
*self.size.write().unwrap() = Point::new(width, height);
self
}
pub fn background_color(mut self, color: Color) -> Self {
self.background_color = color;
self
}
pub fn draggable(mut self, draggable: bool) -> Self {
self.draggable = draggable;
self
}
pub fn shadow(mut self, shadow: bool) -> Self {
self.shadow = shadow;
self
}
pub fn show(&self) {
*self.visible.write().unwrap() = true;
}
pub fn hide(&self) {
*self.visible.write().unwrap() = false;
}
pub fn is_visible(&self) -> bool {
*self.visible.read().unwrap()
}
pub fn get_position(&self) -> Point {
*self.position.read().unwrap()
}
pub fn set_position(&self, pos: Point) {
*self.position.write().unwrap() = pos;
}
fn floating_bounds(&self) -> Rect {
let pos = *self.position.read().unwrap();
let size = *self.size.read().unwrap();
Rect::new(pos.x, pos.y, pos.x + size.x, pos.y + size.y)
}
}
impl Default for Floating {
fn default() -> Self {
Self::new()
}
}
impl Element for Floating {
fn limits(&self, _ctx: &BasicContext) -> ViewLimits {
ViewLimits::fixed(0.0, 0.0)
}
fn stretch(&self) -> ViewStretch {
ViewStretch::new(0.0, 0.0)
}
fn draw(&self, ctx: &Context) {
if !self.is_visible() {
return;
}
let bounds = self.floating_bounds();
let mut canvas = ctx.canvas.borrow_mut();
if self.shadow {
let shadow_rect = bounds.translate(4.0, 4.0);
canvas.fill_style(Color::new(0.0, 0.0, 0.0, 0.3));
canvas.fill_round_rect(shadow_rect, self.corner_radius);
}
canvas.fill_style(self.background_color);
canvas.fill_round_rect(bounds, self.corner_radius);
canvas.stroke_style(self.border_color);
canvas.line_width(1.0);
canvas.begin_path();
canvas.add_round_rect(bounds, self.corner_radius);
canvas.stroke();
drop(canvas);
if let Some(ref content) = self.content {
let inset = 8.0;
let content_bounds = bounds.inset(inset, inset);
let content_ctx = ctx.with_bounds(content_bounds);
content.draw(&content_ctx);
}
}
fn hit_test(&self, ctx: &Context, p: Point, leaf: bool, control: bool) -> Option<&dyn Element> {
if !self.is_visible() {
return None;
}
let bounds = self.floating_bounds();
if bounds.contains(p) {
if let Some(ref content) = self.content {
let inset = 8.0;
let content_bounds = bounds.inset(inset, inset);
let content_ctx = ctx.with_bounds(content_bounds);
if let Some(hit) = content.hit_test(&content_ctx, p, leaf, control) {
return Some(hit);
}
}
Some(self)
} else {
None
}
}
fn wants_control(&self) -> bool {
self.is_visible() && self.draggable
}
fn handle_click(&self, ctx: &Context, btn: MouseButton) -> bool {
if !self.is_visible() || btn.button != MouseButtonKind::Left {
return false;
}
let bounds = self.floating_bounds();
if btn.down {
if bounds.contains(btn.pos) {
if let Some(ref content) = self.content {
let inset = 8.0;
let content_bounds = bounds.inset(inset, inset);
let content_ctx = ctx.with_bounds(content_bounds);
if content.handle_click(&content_ctx, btn) {
return true;
}
}
if self.draggable {
*self.dragging.write().unwrap() = true;
let pos = *self.position.read().unwrap();
*self.drag_offset.write().unwrap() = Point::new(btn.pos.x - pos.x, btn.pos.y - pos.y);
}
return true;
}
} else {
*self.dragging.write().unwrap() = false;
if let Some(ref content) = self.content {
let inset = 8.0;
let content_bounds = bounds.inset(inset, inset);
let content_ctx = ctx.with_bounds(content_bounds);
if content.handle_click(&content_ctx, btn) {
return true;
}
}
}
bounds.contains(btn.pos)
}
fn drag(&mut self, _ctx: &Context, btn: MouseButton) {
if *self.dragging.read().unwrap() {
let offset = *self.drag_offset.read().unwrap();
*self.position.write().unwrap() = Point::new(btn.pos.x - offset.x, btn.pos.y - offset.y);
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub fn floating() -> Floating {
Floating::new()
}