Skip to main content

base_ui/widget/widgets/
shape.rs

1use log::debug;
2
3use crate::animation::animation::{ FadeAnimation, Vec2Animation };
4use crate::graphics::Renderer;
5use crate::style::color::Color;
6use crate::widget::Widget;
7use crate::Animation;
8use std::cell::RefCell;
9use std::sync::Arc;
10
11#[derive(Debug)]
12pub enum ShapeType {
13    Rectangle,
14    Circle,
15    Triangle,
16}
17
18pub struct Shape {
19    x: f32,
20    y: f32,
21    width: f32,
22    height: f32,
23    shape_type: ShapeType,
24    fill_color: Color,
25    border_color: Color,
26    border_width: f32,
27    opacity: f32,
28    is_hovered: bool,
29    is_pressed: bool,
30    position_animation: Option<Vec2Animation>,
31    fade_animation: Option<FadeAnimation>,
32    on_hover: Option<Arc<RefCell<dyn FnMut(bool) + 'static>>>,
33    on_click: Option<Arc<RefCell<dyn FnMut() + 'static>>>,
34}
35
36impl Shape {
37    pub fn new(shape_type: ShapeType) -> Self {
38        debug!("Creating new shape: {:?}", shape_type);
39        Self {
40            x: 0.0,
41            y: 0.0,
42            width: 100.0,
43            height: 100.0,
44            shape_type,
45            fill_color: Color::new(1.0, 1.0, 1.0, 1.0),
46            border_color: Color::new(0.0, 0.0, 0.0, 1.0),
47            border_width: 0.0,
48            opacity: 1.0,
49            is_hovered: false,
50            is_pressed: false,
51            position_animation: None,
52            fade_animation: None,
53            on_hover: None,
54            on_click: None,
55        }
56    }
57
58    pub fn set_fill_color(&mut self, color: Color) {
59        debug!("Setting fill color: {:?}", color);
60        self.fill_color = color;
61    }
62
63    pub fn set_border_color(&mut self, color: Color) {
64        debug!("Setting border color: {:?}", color);
65        self.border_color = color;
66    }
67
68    pub fn set_border_width(&mut self, width: f32) {
69        debug!("Setting border width: {}", width);
70        self.border_width = width;
71    }
72
73    // hover 상태 업데이트
74    pub fn update_hover(&mut self, x: f32, y: f32) {
75        // 이전 상태를 저장
76        let was_hovered = self.is_hovered;
77
78        // 현재 위치가 이전과 같은 상태면 계산 스킵
79        let is_now_hovered = self.contains_point(x, y);
80        if was_hovered == is_now_hovered {
81            return;
82        }
83
84        self.is_hovered = is_now_hovered;
85        if let Some(callback) = &self.on_hover {
86            callback.borrow_mut()(self.is_hovered);
87        }
88    }
89
90    pub fn has_fade_animation(&self) -> bool {
91        self.fade_animation.is_some()
92    }
93
94    pub fn has_position_animation(&self) -> bool {
95        self.position_animation.is_some()
96    }
97}
98
99impl Widget for Shape {
100    fn draw(&self, renderer: &mut Renderer, _screen_width: f32, _screen_height: f32) {
101        // opacity를 적용한 색상 계산
102        let fill_color = self.fill_color.with_opacity(self.opacity);
103        let border_color = self.border_color.with_opacity(self.opacity);
104
105        match self.shape_type {
106            ShapeType::Rectangle => {
107                // 테두리 그리기
108                if self.border_width > 0.0 {
109                    renderer.draw_rect(
110                        self.x - self.border_width,
111                        self.y - self.border_width,
112                        self.width + self.border_width * 2.0,
113                        self.height + self.border_width * 2.0,
114                        border_color.to_array()
115                    );
116                }
117                // 사각형 내부 그리기
118                renderer.draw_rect(self.x, self.y, self.width, self.height, fill_color.to_array());
119            }
120            ShapeType::Circle => {
121                self.draw_circle_with_opacity(renderer, fill_color, border_color)
122            }
123            ShapeType::Triangle => {
124                self.draw_triangle_with_opacity(renderer, fill_color, border_color)
125            }
126        }
127    }
128
129    // Widget trait의 나머지 필수 메서드들 구현
130    fn get_position(&self) -> (f32, f32) {
131        (self.x, self.y)
132    }
133    fn get_size(&self) -> (f32, f32) {
134        (self.width, self.height)
135    }
136    fn get_background_color(&self) -> Color {
137        self.fill_color
138    }
139    fn get_text_color(&self) -> Color {
140        Color::new(0.0, 0.0, 0.0, 0.0)
141    }
142    fn get_hover_background_color(&self) -> Color {
143        self.fill_color
144    }
145    fn get_hover_text_color(&self) -> Color {
146        Color::new(0.0, 0.0, 0.0, 0.0)
147    }
148    fn get_opacity(&self) -> f32 {
149        self.opacity
150    }
151    fn get_is_hovered(&self) -> bool {
152        self.is_hovered
153    }
154    fn get_is_pressed(&self) -> bool {
155        self.is_pressed
156    }
157    fn set_position(&mut self, x: f32, y: f32) {
158        self.x = x;
159        self.y = y;
160    }
161    fn set_size(&mut self, width: f32, height: f32) {
162        self.width = width;
163        self.height = height;
164    }
165    fn position(&self) -> (f32, f32) {
166        (self.x, self.y)
167    }
168    fn size(&self) -> (f32, f32) {
169        (self.width, self.height)
170    }
171
172    fn set_position_animation(&mut self, animation: Vec2Animation) {
173        self.position_animation = Some(animation);
174    }
175
176    fn set_fade_animation(&mut self, animation: FadeAnimation) {
177        self.fade_animation = Some(animation);
178    }
179
180    fn set_on_click<F>(&mut self, callback: F) where F: FnMut() + 'static {
181        self.on_click = Some(Arc::new(RefCell::new(callback)));
182    }
183
184    fn set_on_hover<F>(&mut self, callback: F) where F: FnMut(bool) + 'static {
185        self.on_hover = Some(Arc::new(RefCell::new(callback)));
186    }
187
188    fn on_mouse_press(&mut self, x: f32, y: f32) -> bool {
189        if self.contains_point(x, y) {
190            self.is_pressed = true;
191            true
192        } else {
193            false
194        }
195    }
196
197    fn on_mouse_release(&mut self, x: f32, y: f32) -> bool {
198        if self.is_pressed && self.contains_point(x, y) {
199            if let Some(callback) = &self.on_click {
200                callback.borrow_mut()();
201            }
202            true
203        } else {
204            false
205        }
206    }
207
208    fn update_animations(&mut self, delta_time: f32) {
209        // Update position animation
210        if let Some(ref mut anim) = self.position_animation {
211            anim.update(delta_time);
212            let pos = anim.value();
213            self.x = pos.x;
214            self.y = pos.y;
215
216            if anim.is_finished() {
217                self.position_animation = None;
218            }
219        }
220
221        // Update fade animation
222        if let Some(ref mut anim) = self.fade_animation {
223            anim.update(delta_time);
224            self.opacity = anim.value();
225
226            if anim.is_finished() {
227                self.fade_animation = None;
228            }
229        }
230    }
231}
232
233impl Shape {
234    fn draw_circle_with_opacity(
235        &self,
236        renderer: &mut Renderer,
237        fill_color: Color,
238        border_color: Color
239    ) {
240        let center_x = self.x + self.width / 2.0;
241        let center_y = self.y + self.height / 2.0;
242        let radius = self.width.min(self.height) / 2.0;
243
244        // 테두리가 있을 때만 테두리 그리기
245        if self.border_width > 0.0 {
246            renderer.draw_circle(
247                center_x,
248                center_y,
249                radius + self.border_width,
250                border_color.to_array()
251            );
252        }
253
254        // 내부 채우기
255        renderer.draw_circle(center_x, center_y, radius, fill_color.to_array());
256    }
257
258    fn draw_triangle_with_opacity(
259        &self,
260        renderer: &mut Renderer,
261        fill_color: Color,
262        border_color: Color
263    ) {
264        let vertices = [
265            (self.x, self.y + self.height),
266            (self.x + self.width, self.y + self.height),
267            (self.x + self.width / 2.0, self.y),
268        ];
269
270        // 테두리가 있을 때만 테두리 그리기
271        if self.border_width > 0.0 {
272            let border_vertices = [
273                (vertices[0].0 - self.border_width, vertices[0].1 + self.border_width),
274                (vertices[1].0 + self.border_width, vertices[1].1 + self.border_width),
275                (vertices[2].0, vertices[2].1 - self.border_width),
276            ];
277            renderer.draw_triangle(border_vertices, border_color.to_array());
278        }
279
280        renderer.draw_triangle(vertices, fill_color.to_array());
281    }
282}