1 /*
2 * Copyright 2019, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Author:
6 * Preetpal Kaur <preetpalok123@gmail.com>
7 */
8
9
10 #include "MouseView.h"
11
12 #include <algorithm>
13
14 #include <Box.h>
15 #include <Button.h>
16 #include <Debug.h>
17 #include <GradientLinear.h>
18 #include <GradientRadial.h>
19 #include <MenuField.h>
20 #include <MenuItem.h>
21 #include <PopUpMenu.h>
22 #include <Region.h>
23 #include <Shape.h>
24 #include <Slider.h>
25 #include <TextControl.h>
26 #include <TranslationUtils.h>
27 #include <TranslatorFormats.h>
28 #include <Window.h>
29
30 #include "InputConstants.h"
31 #include "MouseSettings.h"
32
33
34 static const int32 kButtonTop = 3;
35 static const int32 kMouseDownWidth = 72;
36 static const int32 kMouseDownHeight = 35;
37
38 #define W kMouseDownWidth / 100
39 static const int32 kButtonOffsets[][7] = {
40 { 0, 100 * W },
41 { 0, 50 * W, 100 * W },
42 { 0, 35 * W, 65 * W, 100 * W },
43 { 0, 25 * W, 50 * W, 75 * W, 100 * W },
44 { 0, 20 * W, 40 * W, 60 * W, 80 * W, 100 * W },
45 { 0, 19 * W, 34 * W, 50 * W, 66 * W, 82 * W, 100 * W }
46 };
47 #undef W
48
49 static const rgb_color kButtonTextColor = {0, 0, 0, 255};
50 static const rgb_color kMouseShadowColor = {100, 100, 100, 128};
51 static const rgb_color kMouseBodyTopColor = {0xed, 0xed, 0xed, 255};
52 static const rgb_color kMouseBodyBottomColor = {0x85, 0x85, 0x85, 255};
53 static const rgb_color kMouseOutlineColor = {0x51, 0x51, 0x51, 255};
54 static const rgb_color kMouseButtonOutlineColor = {0xa0, 0xa0, 0xa0, 255};
55 static const rgb_color kButtonPressedColor = {110, 110, 110, 110};
56
57
58 static const int32*
getButtonOffsets(int32 type)59 getButtonOffsets(int32 type)
60 {
61 if ((type - 1) >= (int32)B_COUNT_OF(kButtonOffsets))
62 return kButtonOffsets[2];
63 return kButtonOffsets[type - 1];
64 }
65
66
67 static uint32
getMappingNumber(uint32 mapping)68 getMappingNumber(uint32 mapping)
69 {
70 if (mapping == 0)
71 return 0;
72
73 int i;
74 for (i = 0; mapping != 1; i++)
75 mapping >>= 1;
76
77 return i;
78 }
79
80
MouseView(const MouseSettings & settings)81 MouseView::MouseView(const MouseSettings& settings)
82 :
83 BView("Mouse", B_PULSE_NEEDED | B_WILL_DRAW),
84 fSettings(settings),
85 fType(-1),
86 fButtons(0),
87 fOldButtons(0)
88 {
89 SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
90 fScaling = std::max(1.0f, be_plain_font->Size() / 7.0f);
91 }
92
93
~MouseView()94 MouseView::~MouseView()
95 {
96 }
97
98
99 void
SetMouseType(int32 type)100 MouseView::SetMouseType(int32 type)
101 {
102 fType = type;
103 Invalidate();
104 }
105
106
107 void
MouseMapUpdated()108 MouseView::MouseMapUpdated()
109 {
110 Invalidate();
111 }
112
113
114 void
UpdateFromSettings()115 MouseView::UpdateFromSettings()
116 {
117 if (fSettings.MouseType() > 6)
118 debugger("Mouse type is invalid");
119 SetMouseType(fSettings.MouseType());
120 }
121
122
123 void
GetPreferredSize(float * _width,float * _height)124 MouseView::GetPreferredSize(float* _width, float* _height)
125 {
126 if (_width != NULL)
127 *_width = fScaling * (kMouseDownWidth + 2);
128 if (_height != NULL)
129 *_height = fScaling * 104;
130 }
131
132
133 void
AttachedToWindow()134 MouseView::AttachedToWindow()
135 {
136 AdoptParentColors();
137
138 UpdateFromSettings();
139 _CreateButtonsPicture();
140
141 font_height fontHeight;
142 GetFontHeight(&fontHeight);
143 fDigitHeight = int32(ceilf(fontHeight.ascent) + ceilf(fontHeight.descent));
144 fDigitBaseline = int32(ceilf(fontHeight.ascent));
145 }
146
147
148 void
MouseUp(BPoint)149 MouseView::MouseUp(BPoint)
150 {
151 fButtons = 0;
152 Invalidate(_ButtonsRect());
153 fOldButtons = fButtons;
154 }
155
156
157 void
MouseDown(BPoint where)158 MouseView::MouseDown(BPoint where)
159 {
160 BMessage* mouseMsg = Window()->CurrentMessage();
161 fButtons = mouseMsg->FindInt32("buttons");
162 int32 modifiers = mouseMsg->FindInt32("modifiers");
163 if (modifiers & B_CONTROL_KEY) {
164 if (modifiers & B_COMMAND_KEY)
165 fButtons = B_TERTIARY_MOUSE_BUTTON;
166 else
167 fButtons = B_SECONDARY_MOUSE_BUTTON;
168 }
169 // Get the current clipping region before requesting any updates.
170 // Otherwise those parts would be excluded from the region.
171 BRegion clipping;
172 GetClippingRegion(&clipping);
173
174 if (fOldButtons != fButtons) {
175 Invalidate(_ButtonsRect());
176 fOldButtons = fButtons;
177 }
178
179 const int32* offset = getButtonOffsets(fType);
180 int32 button = -1;
181 for (int32 i = 0; i <= fType; i++) {
182 if (_ButtonRect(offset, i).Contains(where)) {
183 button = i;
184 break;
185 }
186 }
187 if (button < 0)
188 return;
189
190 if (clipping.Contains(where)) {
191 button = _ConvertFromVisualOrder(button);
192
193 BPopUpMenu menu("Mouse Map Menu");
194 BMessage message(kMsgMouseMap);
195 message.AddInt32("button", button);
196
197 for (int i = 1; i < 7; i++) {
198 char tmp[2];
199 sprintf(tmp, "%d", i);
200 menu.AddItem(new BMenuItem(tmp, new BMessage(message)));
201 }
202
203 int32 mapping = fSettings.Mapping(button);
204 BMenuItem* item = menu.ItemAt(getMappingNumber(mapping));
205 if (item)
206 item->SetMarked(true);
207 menu.SetTargetForItems(Window());
208
209 ConvertToScreen(&where);
210 menu.Go(where, true);
211 }
212 }
213
214
215 void
Draw(BRect updateFrame)216 MouseView::Draw(BRect updateFrame)
217 {
218 SetDrawingMode(B_OP_ALPHA);
219 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
220 SetScale(fScaling * 1.8);
221
222 BShape mouseShape;
223 mouseShape.MoveTo(BPoint(16, 12));
224 // left
225 BPoint control[3] = {BPoint(12, 16), BPoint(8, 64), BPoint(32, 64)};
226 mouseShape.BezierTo(control);
227 // right
228 BPoint control2[3] = {BPoint(56, 64), BPoint(52, 16), BPoint(48, 12)};
229 mouseShape.BezierTo(control2);
230 // top
231 BPoint control3[3] = {BPoint(44, 8), BPoint(20, 8), BPoint(16, 12)};
232 mouseShape.BezierTo(control3);
233 mouseShape.Close();
234
235 // Draw the shadow
236 SetOrigin(-17 * fScaling, -11 * fScaling);
237 SetHighColor(kMouseShadowColor);
238 FillShape(&mouseShape, B_SOLID_HIGH);
239
240 // Draw the body
241 SetOrigin(-21 * fScaling, -14 * fScaling);
242 BGradientRadial bodyGradient(28, 24, 128);
243 bodyGradient.AddColor(kMouseBodyTopColor, 0);
244 bodyGradient.AddColor(kMouseBodyBottomColor, 255);
245
246 FillShape(&mouseShape, bodyGradient);
247
248 // Draw the outline
249 SetPenSize(1 / 1.8 / fScaling);
250 SetDrawingMode(B_OP_OVER);
251 SetHighColor(kMouseOutlineColor);
252
253 StrokeShape(&mouseShape, B_SOLID_HIGH);
254
255 // bottom button border
256 BShape buttonsOutline;
257 buttonsOutline.MoveTo(BPoint(13, 27));
258 BPoint control4[] = {BPoint(18, 30), BPoint(46, 30), BPoint(51, 27)};
259 buttonsOutline.BezierTo(control4);
260
261 SetHighColor(kMouseButtonOutlineColor);
262 StrokeShape(&buttonsOutline, B_SOLID_HIGH);
263
264 SetScale(1);
265 SetOrigin(0, 0);
266
267 mouse_map map;
268 fSettings.Mapping(map);
269
270 SetDrawingMode(B_OP_OVER);
271
272 // All button drawing is clipped to the outline of the buttons area,
273 // simplifying the code below as it can overdraw things.
274 ClipToPicture(&fButtonsPicture, B_ORIGIN, false);
275
276 // Separator between the buttons
277 const int32* offset = getButtonOffsets(fType);
278 for (int32 i = 1; i < fType; i++) {
279 BRect buttonRect = _ButtonRect(offset, i);
280 StrokeLine(buttonRect.LeftTop(), buttonRect.LeftBottom());
281 }
282
283 for (int32 i = 0; i < fType; i++) {
284 // draw mapping number centered over the button
285
286 bool pressed = (fButtons & map.button[_ConvertFromVisualOrder(i)]) != 0;
287 // is button currently pressed?
288 if (pressed) {
289 SetDrawingMode(B_OP_ALPHA);
290 SetHighColor(kButtonPressedColor);
291 FillRect(_ButtonRect(offset, i));
292 }
293
294 BRect border(fScaling * (offset[i] + 1), fScaling * (kButtonTop + 5),
295 fScaling * offset[i + 1] - 1,
296 fScaling * (kButtonTop + kMouseDownHeight - 4));
297 if (i == 0)
298 border.left += fScaling * 5;
299 if (i == fType - 1)
300 border.right -= fScaling * 4;
301
302 char label[2] = {0};
303 int32 number = getMappingNumber(map.button[_ConvertFromVisualOrder(i)]);
304 label[0] = number + '1';
305
306 SetDrawingMode(B_OP_OVER);
307 SetHighColor(kButtonTextColor);
308 DrawString(label,
309 BPoint(border.left + (border.Width() - StringWidth(label)) / 2,
310 border.top + fDigitBaseline
311 + (border.IntegerHeight() - fDigitHeight) / 2));
312 }
313
314 ClipToPicture(NULL);
315 }
316
317
318 BRect
_ButtonsRect() const319 MouseView::_ButtonsRect() const
320 {
321 return BRect(0, fScaling * kButtonTop, fScaling * kMouseDownWidth,
322 fScaling * (kButtonTop + kMouseDownHeight));
323 }
324
325
326 BRect
_ButtonRect(const int32 * offsets,int index) const327 MouseView::_ButtonRect(const int32* offsets, int index) const
328 {
329 return BRect(fScaling * offsets[index], fScaling * kButtonTop,
330 fScaling * offsets[index + 1] - 1,
331 fScaling * (kButtonTop + kMouseDownHeight));
332 }
333
334
335 /** The buttons on a mouse are normally 1 (left), 2 (right), 3 (middle) so
336 * we need to reorder them */
337 int32
_ConvertFromVisualOrder(int32 i)338 MouseView::_ConvertFromVisualOrder(int32 i)
339 {
340 if (fType < 3)
341 return i;
342
343 switch (i) {
344 case 0:
345 return 0;
346 case 1:
347 return 2;
348 case 2:
349 return 1;
350 default:
351 return i;
352 }
353 }
354
355
356 void
_CreateButtonsPicture()357 MouseView::_CreateButtonsPicture()
358 {
359 BeginPicture(&fButtonsPicture);
360 SetScale(1.8 * fScaling);
361 SetOrigin(-21 * fScaling, -14 * fScaling);
362
363 BShape mouseShape;
364 mouseShape.MoveTo(BPoint(48, 12));
365 // top
366 BPoint control3[3] = {BPoint(44, 8), BPoint(20, 8), BPoint(16, 12)};
367 mouseShape.BezierTo(control3);
368 // left
369 BPoint control[3] = {BPoint(12, 16), BPoint(13, 27), BPoint(13, 27)};
370 mouseShape.BezierTo(control);
371 // bottom
372 BPoint control4[3] = {BPoint(18, 30), BPoint(46, 30), BPoint(51, 27)};
373 mouseShape.BezierTo(control4);
374 // right
375 BPoint control2[3] = {BPoint(51, 27), BPoint(50, 14), BPoint(48, 12)};
376 mouseShape.BezierTo(control2);
377
378 mouseShape.Close();
379
380 SetHighColor(255, 0, 0, 255);
381 FillShape(&mouseShape, B_SOLID_HIGH);
382
383 EndPicture();
384 SetOrigin(0, 0);
385 SetScale(1);
386 }
387