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