1 /** 2 * Push buttons and the two-state / grouped button controls. 3 * 4 * All four classes here wrap the Win32 `"BUTTON"` class with different styles: 5 * `Button` is a plain push button, `CheckBox` an auto check box, and 6 * `RadioButton` an auto radio button (optionally starting a new group). Each is 7 * interactive (`WS_TABSTOP`) and exposes a delegate event fired on click. 8 */ 9 module deft.controls.button; 10 11 version (Windows): 12 13 import core.sys.windows.windows; 14 15 import deft.controls.control; 16 import deft.events; 17 import deft.widget; 18 19 /// A standard clickable push button. 20 class Button : Control 21 { 22 /// Fired when the button is clicked (`BN_CLICKED`). 23 Event!() onClicked; 24 25 /// Create a push button captioned `text` inside `parent`. 26 this(Widget parent, string text) 27 { 28 super(parent, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP); 29 setText(text); 30 } 31 32 /// Fire `onClicked` on a `BN_CLICKED` notification. 33 override bool processCommand(ushort notificationCode) 34 { 35 if (notificationCode == BN_CLICKED) 36 { 37 onClicked.fire(); 38 return true; 39 } 40 return false; 41 } 42 43 /// Buttons prefer a modest fixed size. 44 override Size getPreferredSize() 45 { 46 return Size(100, 30); 47 } 48 } 49 50 /// A labeled two-state check box. 51 class CheckBox : Control 52 { 53 /// Fired when the check box is toggled (`BN_CLICKED`). 54 Event!() onToggled; 55 56 /// Create a check box captioned `text` inside `parent`. 57 this(Widget parent, string text) 58 { 59 super(parent, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP); 60 setText(text); 61 } 62 63 /// Whether the box is currently checked. 64 bool isChecked() 65 { 66 return SendMessageW(handle, BM_GETCHECK, 0, 0) == BST_CHECKED; 67 } 68 69 /// Set the checked state. 70 void setChecked(bool value) 71 { 72 SendMessageW(handle, BM_SETCHECK, value ? BST_CHECKED : BST_UNCHECKED, 0); 73 } 74 75 /// Fire `onToggled` on a `BN_CLICKED` notification. 76 override bool processCommand(ushort notificationCode) 77 { 78 if (notificationCode == BN_CLICKED) 79 { 80 onToggled.fire(); 81 return true; 82 } 83 return false; 84 } 85 86 /// Check boxes prefer room for their caption. 87 override Size getPreferredSize() 88 { 89 return Size(120, 24); 90 } 91 } 92 93 /// A labeled radio button; one selection per group. 94 class RadioButton : Control 95 { 96 /// Fired when the radio button is selected (`BN_CLICKED`). 97 Event!() onSelected; 98 99 /** 100 * Create a radio button captioned `text` inside `parent`. 101 * 102 * Pass `firstInGroup = true` for the first button of a group. That button is 103 * the group's single tab stop (`WS_TABSTOP`) and starts the group; the 104 * remaining buttons are reached with the arrow keys, not Tab. A non-first 105 * button therefore drops both its tab stop and the `WS_GROUP` the control base 106 * adds by default, so it continues the previous button's group instead of 107 * starting a new one. End the group by giving the next control `WS_GROUP` 108 * (every Deft control has it by default, so a following non-radio control 109 * terminates the group automatically). 110 */ 111 this(Widget parent, string text, bool firstInGroup = false) 112 { 113 DWORD style = BS_AUTORADIOBUTTON; 114 if (firstInGroup) 115 style |= WS_TABSTOP; // WS_GROUP comes from the control base 116 super(parent, "BUTTON", style); 117 118 if (!firstInGroup) 119 { 120 // Continue the previous radio button's group: a continuation button is 121 // not a tab stop and must not start a new group. 122 auto s = GetWindowLongW(handle, GWL_STYLE); 123 SetWindowLongW(handle, GWL_STYLE, s & ~WS_GROUP); 124 } 125 126 setText(text); 127 } 128 129 /// Whether this radio button is currently selected. 130 bool isChecked() 131 { 132 return SendMessageW(handle, BM_GETCHECK, 0, 0) == BST_CHECKED; 133 } 134 135 /// Set the selected state. 136 void setChecked(bool value) 137 { 138 SendMessageW(handle, BM_SETCHECK, value ? BST_CHECKED : BST_UNCHECKED, 0); 139 } 140 141 /// Fire `onSelected` on a `BN_CLICKED` notification. 142 override bool processCommand(ushort notificationCode) 143 { 144 if (notificationCode == BN_CLICKED) 145 { 146 onSelected.fire(); 147 return true; 148 } 149 return false; 150 } 151 152 /// Radio buttons prefer room for their caption. 153 override Size getPreferredSize() 154 { 155 return Size(120, 24); 156 } 157 }