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