1use winapi::um::{
2 winuser::{WS_VISIBLE, WS_DISABLED, SS_WORDELLIPSIS},
3 wingdi::DeleteObject
4};
5
6use winapi::shared::windef::HBRUSH;
7use crate::win32::window_helper as wh;
8use crate::win32::base_helper::check_hwnd;
9use crate::{Font, NwgError, HTextAlign, VTextAlign, RawEventHandler, unbind_raw_event_handler};
10use super::{ControlBase, ControlHandle};
11use std::cell::RefCell;
12
13const NOT_BOUND: &'static str = "Label is not yet bound to a winapi object";
14const BAD_HANDLE: &'static str = "INTERNAL ERROR: Label handle is not HWND!";
15
16bitflags! {
17 pub struct LabelFlags: u32 {
25 const NONE = 0;
26 const VISIBLE = WS_VISIBLE;
27 const DISABLED = WS_DISABLED;
28
29 const ELIPSIS = SS_WORDELLIPSIS;
31 }
32}
33
34#[derive(Default)]
74pub struct Label {
75 pub handle: ControlHandle,
76 background_brush: Option<HBRUSH>,
77 handler0: RefCell<Option<RawEventHandler>>,
78 handler1: RefCell<Option<RawEventHandler>>,
79}
80
81impl Label {
82
83 pub fn builder<'a>() -> LabelBuilder<'a> {
84 LabelBuilder {
85 text: "A label",
86 size: (130, 25),
87 position: (0, 0),
88 flags: None,
89 ex_flags: 0,
90 font: None,
91 parent: None,
92 h_align: HTextAlign::Left,
93 v_align: VTextAlign::Center,
94 background_color: None
95 }
96 }
97
98 pub fn font(&self) -> Option<Font> {
100 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
101
102 let font_handle = wh::get_window_font(handle);
103 if font_handle.is_null() {
104 None
105 } else {
106 Some(Font { handle: font_handle })
107 }
108 }
109
110 pub fn set_font(&self, font: Option<&Font>) {
112 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
113 unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
114 }
115
116 pub fn focus(&self) -> bool {
118 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
119 unsafe { wh::get_focus(handle) }
120 }
121
122 pub fn set_focus(&self) {
124 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
125 unsafe { wh::set_focus(handle); }
126 }
127
128 pub fn enabled(&self) -> bool {
130 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
131 unsafe { wh::get_window_enabled(handle) }
132 }
133
134 pub fn set_enabled(&self, v: bool) {
136 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
137 unsafe { wh::set_window_enabled(handle, v) }
138 }
139
140 pub fn visible(&self) -> bool {
143 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
144 unsafe { wh::get_window_visibility(handle) }
145 }
146
147 pub fn set_visible(&self, v: bool) {
149 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
150 unsafe { wh::set_window_visibility(handle, v) }
151 }
152
153 pub fn size(&self) -> (u32, u32) {
155 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156 unsafe { wh::get_window_size(handle) }
157 }
158
159 pub fn set_size(&self, x: u32, y: u32) {
161 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
162 unsafe { wh::set_window_size(handle, x, y, false) }
163 }
164
165 pub fn position(&self) -> (i32, i32) {
167 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
168 unsafe { wh::get_window_position(handle) }
169 }
170
171 pub fn set_position(&self, x: i32, y: i32) {
173 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
174 unsafe { wh::set_window_position(handle, x, y) }
175 }
176
177 pub fn text(&self) -> String {
179 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
180 unsafe { wh::get_window_text(handle) }
181 }
182
183 pub fn set_text<'a>(&self, v: &'a str) {
185 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
186 unsafe { wh::set_window_text(handle, v) }
187 }
188
189 pub fn class_name(&self) -> &'static str {
191 "STATIC"
192 }
193
194 pub fn flags(&self) -> u32 {
196 use winapi::um::winuser::{SS_NOPREFIX, SS_LEFT};
197
198 WS_VISIBLE | SS_NOPREFIX | SS_LEFT
199 }
200
201 pub fn forced_flags(&self) -> u32 {
203 use winapi::um::winuser::{SS_NOTIFY, WS_CHILD};
204
205 WS_CHILD | SS_NOTIFY
206 }
207
208 fn hook_non_client_size(&mut self, bg: Option<[u8; 3]>, v_align: VTextAlign) {
210 use crate::bind_raw_event_handler_inner;
211 use winapi::shared::windef::{HWND, HGDIOBJ, RECT, POINT};
212 use winapi::shared::{basetsd::UINT_PTR, minwindef::LRESULT};
213 use winapi::um::winuser::{WM_CTLCOLORSTATIC, WM_NCCALCSIZE, WM_NCPAINT, WM_SIZE, DT_CALCRECT, DT_LEFT, NCCALCSIZE_PARAMS, COLOR_WINDOW};
214 use winapi::um::winuser::{SWP_NOOWNERZORDER, SWP_NOSIZE, SWP_NOMOVE, SWP_FRAMECHANGED};
215 use winapi::um::winuser::{GetDC, DrawTextW, ReleaseDC, GetClientRect, GetWindowRect, FillRect, ScreenToClient, SetWindowPos, GetWindowTextW, GetWindowTextLengthW};
216 use winapi::um::wingdi::{SelectObject, CreateSolidBrush, RGB};
217 use std::{mem, ptr};
218
219 if self.handle.blank() { panic!("{}", NOT_BOUND); }
220 let handle = self.handle.hwnd().expect(BAD_HANDLE);
221
222 let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
223
224 let brush = match bg {
225 Some(c) => {
226 let b = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
227 self.background_brush = Some(b);
228 b
229 },
230 None => COLOR_WINDOW as HBRUSH
231 };
232
233 unsafe {
234
235 if bg.is_some() {
236 let handler0 = bind_raw_event_handler_inner(&parent_handle, handle as UINT_PTR, move |_hwnd, msg, _w, l| {
237 match msg {
238 WM_CTLCOLORSTATIC => {
239 let child = l as HWND;
240 if child == handle {
241 return Some(brush as LRESULT);
242 }
243 },
244 _ => {}
245 }
246
247 None
248 });
249
250 *self.handler0.borrow_mut() = Some(handler0.unwrap());
251 }
252
253 let handler1 = bind_raw_event_handler_inner(&self.handle, 0, move |hwnd, msg, w, l| {
254 match msg {
255 WM_NCCALCSIZE => {
256 if w == 0 { return None }
257
258 let font_handle = wh::get_window_font(hwnd);
260 let mut r: RECT = mem::zeroed();
261 let dc = GetDC(hwnd);
262
263 let old = SelectObject(dc, font_handle as HGDIOBJ);
264
265 let mut newline_count = 1;
266 let buffer_size = GetWindowTextLengthW(handle) as usize;
267 match buffer_size == 0 {
268 true => {
269 let calc: [u16;2] = [75, 121];
270 DrawTextW(dc, calc.as_ptr(), 2, &mut r, DT_CALCRECT | DT_LEFT);
271 },
272 false => {
273 let mut buffer: Vec<u16> = vec![0; buffer_size + 1];
274 if GetWindowTextW(handle, buffer.as_mut_ptr(), buffer_size as _) == 0 {
275 let calc: [u16;2] = [75, 121];
276 DrawTextW(dc, calc.as_ptr(), 2, &mut r, DT_CALCRECT | DT_LEFT);
277 } else {
278 for &c in buffer.iter() {
279 if c == b'\n' as u16 {
280 newline_count += 1;
281 }
282 }
283 DrawTextW(dc, buffer.as_ptr(), 2, &mut r, DT_CALCRECT | DT_LEFT);
284 }
285 }
286 }
287
288 let client_height = r.bottom * newline_count;
289
290 SelectObject(dc, old);
291 ReleaseDC(hwnd, dc);
292
293 let mut client: RECT = mem::zeroed();
295 let mut window: RECT = mem::zeroed();
296 GetClientRect(hwnd, &mut client);
297 GetWindowRect(hwnd, &mut window);
298
299 let window_height = window.bottom - window.top;
300 let info_ptr: *mut NCCALCSIZE_PARAMS = l as *mut NCCALCSIZE_PARAMS;
301 let info = &mut *info_ptr;
302 match v_align {
303 VTextAlign::Top => {
304 info.rgrc[0].bottom -= window_height - client_height;
305 },
306 VTextAlign::Center => {
307 let center = ((window_height - client_height) / 2) - 1;
308 info.rgrc[0].top += center;
309 info.rgrc[0].bottom -= center;
310 },
311 VTextAlign::Bottom => {
312 info.rgrc[0].top += window_height - client_height;
313 },
314 }
315 },
316 WM_NCPAINT => {
317 let mut window: RECT = mem::zeroed();
318 let mut client: RECT = mem::zeroed();
319 GetWindowRect(hwnd, &mut window);
320 GetClientRect(hwnd, &mut client);
321
322 let mut pt1 = POINT {x: window.left, y: window.top};
323 ScreenToClient(hwnd, &mut pt1);
324
325 let mut pt2 = POINT {x: window.right, y: window.bottom};
326 ScreenToClient(hwnd, &mut pt2);
327
328 let top = RECT {
329 left: 0,
330 top: pt1.y,
331 right: client.right,
332 bottom: client.top
333 };
334
335 let bottom = RECT {
336 left: 0,
337 top: client.bottom,
338 right: client.right,
339 bottom: pt2.y
340 };
341
342 let dc = GetDC(hwnd);
343 FillRect(dc, &top, brush);
344 FillRect(dc, &bottom, brush);
345 ReleaseDC(hwnd, dc);
346 },
347 WM_SIZE => {
348 SetWindowPos(hwnd, ptr::null_mut(), 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
349 },
350 _ => {}
351 }
352
353 None
354 });
355
356 *self.handler1.borrow_mut() = Some(handler1.unwrap());
357
358 }
359 }
360
361}
362
363impl PartialEq for Label {
364 fn eq(&self, other: &Self) -> bool {
365 self.handle == other.handle
366 }
367}
368
369
370impl Drop for Label {
371 fn drop(&mut self) {
372 let handler = self.handler0.borrow();
373 if let Some(h) = handler.as_ref() {
374 drop(unbind_raw_event_handler(h));
375 }
376
377 let handler = self.handler1.borrow();
378 if let Some(h) = handler.as_ref() {
379 drop(unbind_raw_event_handler(h));
380 }
381
382 if let Some(bg) = self.background_brush {
383 unsafe { DeleteObject(bg as _); }
384 }
385
386 self.handle.destroy();
387 }
388}
389
390pub struct LabelBuilder<'a> {
391 text: &'a str,
392 size: (i32, i32),
393 position: (i32, i32),
394 background_color: Option<[u8; 3]>,
395 flags: Option<LabelFlags>,
396 ex_flags: u32,
397 font: Option<&'a Font>,
398 h_align: HTextAlign,
399 v_align: VTextAlign,
400 parent: Option<ControlHandle>
401}
402
403impl<'a> LabelBuilder<'a> {
404
405 pub fn flags(mut self, flags: LabelFlags) -> LabelBuilder<'a> {
406 self.flags = Some(flags);
407 self
408 }
409
410 pub fn ex_flags(mut self, flags: u32) -> LabelBuilder<'a> {
411 self.ex_flags = flags;
412 self
413 }
414
415 pub fn text(mut self, text: &'a str) -> LabelBuilder<'a> {
416 self.text = text;
417 self
418 }
419
420 pub fn size(mut self, size: (i32, i32)) -> LabelBuilder<'a> {
421 self.size = size;
422 self
423 }
424
425 pub fn position(mut self, pos: (i32, i32)) -> LabelBuilder<'a> {
426 self.position = pos;
427 self
428 }
429
430 pub fn font(mut self, font: Option<&'a Font>) -> LabelBuilder<'a> {
431 self.font = font;
432 self
433 }
434
435 pub fn background_color(mut self, color: Option<[u8;3]>) -> LabelBuilder<'a> {
436 self.background_color = color;
437 self
438 }
439
440 pub fn h_align(mut self, align: HTextAlign) -> LabelBuilder<'a> {
441 self.h_align = align;
442 self
443 }
444
445 pub fn v_align(mut self, align: VTextAlign) -> LabelBuilder<'a> {
446 self.v_align = align;
447 self
448 }
449
450 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> LabelBuilder<'a> {
451 self.parent = Some(p.into());
452 self
453 }
454
455 pub fn build(self, out: &mut Label) -> Result<(), NwgError> {
456 use winapi::um::winuser::{SS_LEFT, SS_RIGHT, SS_CENTER};
457
458 let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
459
460 match self.h_align {
461 HTextAlign::Left => { flags |= SS_LEFT; },
462 HTextAlign::Right => { flags |= SS_RIGHT; },
463 HTextAlign::Center => { flags |= SS_CENTER; },
464 }
465
466 let parent = match self.parent {
467 Some(p) => Ok(p),
468 None => Err(NwgError::no_parent("Label"))
469 }?;
470
471 *out = Label::default();
473
474 out.handle = ControlBase::build_hwnd()
475 .class_name(out.class_name())
476 .forced_flags(out.forced_flags())
477 .flags(flags)
478 .ex_flags(self.ex_flags)
479 .size(self.size)
480 .position(self.position)
481 .text(self.text)
482 .parent(Some(parent))
483 .build()?;
484
485 if self.font.is_some() {
486 out.set_font(self.font);
487 } else {
488 out.set_font(Font::global_default().as_ref());
489 }
490
491 out.hook_non_client_size(self.background_color, self.v_align);
492
493 Ok(())
494 }
495
496}
497