xref: /haiku/src/preferences/input/MouseView.cpp (revision 02dabf4bd99b67bdec8a6650bef2efd3cb04a9a6)
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