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