1use winapi::um::winuser::{WS_VISIBLE, ES_MULTILINE, WS_DISABLED, EM_SETSEL};
2use crate::win32::window_helper as wh;
3use crate::win32::base_helper::check_hwnd;
4use crate::win32::richedit as rich;
5use crate::{Font, NwgError, RawEventHandler, HTextAlign, unbind_raw_event_handler};
6use super::{ControlBase, ControlHandle, CharFormat, ParaFormat};
7
8use std::{rc::Rc, ops::Range, cell::RefCell};
9
10
11const NOT_BOUND: &'static str = "RichLabel is not yet bound to a winapi object";
12const BAD_HANDLE: &'static str = "INTERNAL ERROR: RichLabel handle is not HWND!";
13
14bitflags! {
15 pub struct RichLabelFlags: u32 {
24 const NONE = 0;
25 const VISIBLE = WS_VISIBLE;
26 const DISABLED = WS_DISABLED;
27 const MULTI_LINE = ES_MULTILINE;
28 }
29}
30
31
32#[derive(Default)]
78pub struct RichLabel {
79 pub handle: ControlHandle,
80 line_height: Rc<RefCell<Option<i32>>>,
81 handler0: RefCell<Option<RawEventHandler>>,
82}
83
84impl RichLabel {
85
86 pub fn builder<'a>() -> RichLabelBuilder<'a> {
87 RichLabelBuilder {
88 text: "A rich label",
89 size: (130, 25),
90 position: (0, 0),
91 flags: None,
92 ex_flags: 0,
93 font: None,
94 h_align: HTextAlign::Left,
95 background_color: None,
96 line_height: None,
97 parent: None,
98 }
99 }
100
101 pub fn set_background_color(&self, color: [u8; 3]) {
104 use winapi::um::wingdi::RGB;
105
106 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
107 let color = RGB(color[0], color[1], color[2]);
108 wh::send_message(handle, rich::EM_SETBKGNDCOLOR, 0, color as _);
109 }
110
111 pub fn set_char_format(&self, r: Range<u32>, fmt: &CharFormat) {
113 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
114 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
115 rich::set_char_format(handle, fmt);
116 wh::send_message(handle, EM_SETSEL as u32, 0, 0);
117 }
118
119 pub fn char_format(&self, r: Range<u32>) -> CharFormat {
121 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
122
123 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
124 let out = rich::char_format(handle);
125 wh::send_message(handle, EM_SETSEL as u32, 0, 0);
126
127 out
128 }
129
130 pub fn set_para_format(&self, r: Range<u32>, fmt: &ParaFormat) {
132 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
133
134 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
135 rich::set_para_format(handle, fmt);
136 wh::send_message(handle, EM_SETSEL as u32, 0, 0);
137 }
138
139 pub fn para_format(&self, r: Range<u32>) -> ParaFormat {
142 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
143
144 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
145 let out = rich::para_format(handle);
146 wh::send_message(handle, EM_SETSEL as u32, 0, 0);
147
148 out
149 }
150
151 pub fn selection(&self) -> Range<usize> {
153 use winapi::um::winuser::EM_GETSEL;
154
155 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156
157 let (mut out1, mut out2) = (0u32, 0u32);
158 let (ptr1, ptr2) = (&mut out1 as *mut u32, &mut out2 as *mut u32);
159 wh::send_message(handle, EM_GETSEL as u32, ptr1 as _, ptr2 as _);
160
161 Range { start: out1 as usize, end: out2 as usize }
162 }
163
164 pub fn set_selection(&self, r: Range<u32>) {
166 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
167 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
168 }
169
170 pub fn len(&self) -> u32 {
173 use winapi::um::winuser::EM_LINELENGTH;
174
175 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
176 wh::send_message(handle, EM_LINELENGTH as u32, 0, 0) as u32
177 }
178
179 pub fn clear(&self) {
181 self.set_text("");
182 }
183
184 pub fn set_line_height(&self, height: Option<i32>) {
186 *self.line_height.borrow_mut() = height;
187 }
188
189 pub fn line_height(&self) -> Option<i32> {
191 *self.line_height.borrow()
192 }
193
194 pub fn set_font(&self, font: Option<&Font>) {
197 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
198 unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
199 }
200
201 pub fn visible(&self) -> bool {
204 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
205 unsafe { wh::get_window_visibility(handle) }
206 }
207
208 pub fn set_visible(&self, v: bool) {
210 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
211 unsafe { wh::set_window_visibility(handle, v) }
212 }
213
214 pub fn size(&self) -> (u32, u32) {
216 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
217 unsafe { wh::get_window_size(handle) }
218 }
219
220 pub fn set_size(&self, x: u32, y: u32) {
222 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
223 unsafe { wh::set_window_size(handle, x, y, false) }
224 }
225
226 pub fn position(&self) -> (i32, i32) {
228 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
229 unsafe { wh::get_window_position(handle) }
230 }
231
232 pub fn set_position(&self, x: i32, y: i32) {
234 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
235 unsafe { wh::set_window_position(handle, x, y) }
236 }
237
238 pub fn text(&self) -> String {
240 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
241 unsafe { wh::get_window_text(handle) }
242 }
243
244 pub fn set_text<'a>(&self, v: &'a str) {
246 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
247 unsafe { wh::set_window_text(handle, v) }
248 }
249
250 pub fn class_name(&self) -> &'static str {
252 "RICHEDIT50W"
253 }
254
255 pub fn flags(&self) -> u32 {
257 WS_VISIBLE
258 }
259
260 pub fn forced_flags(&self) -> u32 {
262 use winapi::um::winuser::{ES_READONLY, WS_CHILD};
263
264 ES_READONLY | WS_CHILD
265 }
266
267 unsafe fn override_events(&self) {
268 use crate::bind_raw_event_handler_inner;
269 use winapi::shared::windef::{RECT, HBRUSH, POINT};
270 use winapi::um::winuser::{WM_NCCALCSIZE, WM_SIZE, WM_NCPAINT};
271 use winapi::um::winuser::{SWP_NOOWNERZORDER, SWP_NOSIZE, SWP_NOMOVE, SWP_FRAMECHANGED, COLOR_WINDOW, NCCALCSIZE_PARAMS};
272 use winapi::um::winuser::{GetDC, ReleaseDC, GetClientRect, GetWindowRect, ScreenToClient, FillRect, SetWindowPos};
273 use std::{mem, ptr};
274
275 let callback_line_height = self.line_height.clone();
276
277 let handler0 = bind_raw_event_handler_inner(&self.handle, 0, move |hwnd, msg, w, l| {
279 match msg {
280 WM_NCCALCSIZE => {
281 let client_height = *callback_line_height.borrow();
282 if w == 0 || client_height.is_none() { return None }
283
284 let client_height = client_height.unwrap();
285
286 let mut client: RECT = mem::zeroed();
288 let mut window: RECT = mem::zeroed();
289 GetClientRect(hwnd, &mut client);
290 GetWindowRect(hwnd, &mut window);
291
292 let window_height = window.bottom - window.top;
293 let center = ((window_height - client_height) / 2) - 1;
294
295 let info_ptr: *mut NCCALCSIZE_PARAMS = l as *mut NCCALCSIZE_PARAMS;
297 let info = &mut *info_ptr;
298
299 info.rgrc[0].top += center;
300 info.rgrc[0].bottom -= center;
301
302 None
303 },
304 WM_NCPAINT => {
305 let client_height = *callback_line_height.borrow();
306 if client_height.is_none() { return None }
307
308 let mut window: RECT = mem::zeroed();
309 let mut client: RECT = mem::zeroed();
310 GetWindowRect(hwnd, &mut window);
311 GetClientRect(hwnd, &mut client);
312
313 let mut pt1 = POINT {x: window.left, y: window.top};
314 ScreenToClient(hwnd, &mut pt1);
315
316 let mut pt2 = POINT {x: window.right, y: window.bottom};
317 ScreenToClient(hwnd, &mut pt2);
318
319 let top = RECT {
320 left: 0,
321 top: pt1.y,
322 right: client.right,
323 bottom: client.top
324 };
325
326 let bottom = RECT {
327 left: 0,
328 top: client.bottom,
329 right: client.right,
330 bottom: pt2.y
331 };
332
333 let dc = GetDC(hwnd);
334 let brush = COLOR_WINDOW as HBRUSH;
335 FillRect(dc, &top, brush);
336 FillRect(dc, &bottom, brush);
337 ReleaseDC(hwnd, dc);
338 None
339 },
340 WM_SIZE => {
341 SetWindowPos(hwnd, ptr::null_mut(), 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
342 None
343 },
344 _ => None
345 }
346 });
347
348 *self.handler0.borrow_mut() = Some(handler0.unwrap());
349 }
350
351}
352
353impl PartialEq for RichLabel {
354 fn eq(&self, other: &Self) -> bool {
355 self.handle == other.handle
356 }
357}
358
359impl Drop for RichLabel {
360 fn drop(&mut self) {
361 let handler = self.handler0.borrow();
362 if let Some(h) = handler.as_ref() {
363 drop(unbind_raw_event_handler(h));
364 }
365
366 self.handle.destroy();
367 }
368}
369
370pub struct RichLabelBuilder<'a> {
371 text: &'a str,
372 size: (i32, i32),
373 position: (i32, i32),
374 flags: Option<RichLabelFlags>,
375 ex_flags: u32,
376 font: Option<&'a Font>,
377 h_align: HTextAlign,
378 background_color: Option<[u8; 3]>,
379 line_height: Option<i32>,
380 parent: Option<ControlHandle>,
381}
382
383impl<'a> RichLabelBuilder<'a> {
384
385 pub fn text(mut self, text: &'a str) -> RichLabelBuilder<'a> {
386 self.text = text;
387 self
388 }
389
390 pub fn size(mut self, size: (i32, i32)) -> RichLabelBuilder<'a> {
391 self.size = size;
392 self
393 }
394
395 pub fn position(mut self, pos: (i32, i32)) -> RichLabelBuilder<'a> {
396 self.position = pos;
397 self
398 }
399
400 pub fn font(mut self, font: Option<&'a Font>) -> RichLabelBuilder<'a> {
401 self.font = font;
402 self
403 }
404
405 pub fn flags(mut self, flags: RichLabelFlags) -> RichLabelBuilder<'a> {
406 self.flags = Some(flags);
407 self
408 }
409
410 pub fn ex_flags(mut self, flags: u32) -> RichLabelBuilder<'a> {
411 self.ex_flags = flags;
412 self
413 }
414
415 pub fn h_align(mut self, align: HTextAlign) -> RichLabelBuilder<'a> {
416 self.h_align = align;
417 self
418 }
419
420 pub fn background_color(mut self, color: Option<[u8; 3]>) -> RichLabelBuilder<'a> {
421 self.background_color = color;
422 self
423 }
424
425 pub fn line_height(mut self, height: Option<i32>) -> RichLabelBuilder<'a> {
426 self.line_height = height;
427 self
428 }
429
430 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> RichLabelBuilder<'a> {
431 self.parent = Some(p.into());
432 self
433 }
434
435 pub fn build(self, out: &mut RichLabel) -> Result<(), NwgError> {
436 use winapi::um::winuser::{SS_LEFT, SS_RIGHT, SS_CENTER};
437
438 let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
439 match self.h_align {
440 HTextAlign::Left => { flags |= SS_LEFT; },
441 HTextAlign::Right => { flags |= SS_RIGHT; },
442 HTextAlign::Center => { flags |= SS_CENTER; },
443 }
444
445 let parent = match self.parent {
446 Some(p) => Ok(p),
447 None => Err(NwgError::no_parent("RichLabel"))
448 }?;
449
450 *out = Default::default();
452
453 *out.line_height.borrow_mut() = self.line_height;
454 out.handle = ControlBase::build_hwnd()
455 .class_name(out.class_name())
456 .forced_flags(out.forced_flags())
457 .flags(flags)
458 .ex_flags(self.ex_flags)
459 .size(self.size)
460 .position(self.position)
461 .text(self.text)
462 .parent(Some(parent))
463 .build()?;
464
465 if self.font.is_some() {
466 out.set_font(self.font);
467 } else {
468 out.set_font(Font::global_default().as_ref());
469 }
470
471 if let Some(color) = self.background_color {
472 out.set_background_color(color);
473 } else {
474 if let Ok(color) = wh::get_background_color(parent.hwnd().unwrap()) {
475 out.set_background_color(color);
476 }
477 }
478
479 unsafe { out.override_events(); }
480
481 Ok(())
482 }
483
484}