xref: /haiku/src/preferences/input/InputTouchpadPrefView.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 "InputTouchpadPrefView.h"
11 
12 #include <stdio.h>
13 
14 #include <Alert.h>
15 #include <Box.h>
16 #include <Catalog.h>
17 #include <CheckBox.h>
18 #include <ControlLook.h>
19 #include <File.h>
20 #include <FindDirectory.h>
21 #include <Input.h>
22 #include <LayoutBuilder.h>
23 #include <Locale.h>
24 #include <MenuField.h>
25 #include <MenuItem.h>
26 #include <Message.h>
27 #include <Path.h>
28 #include <Screen.h>
29 #include <SeparatorView.h>
30 #include <SpaceLayoutItem.h>
31 #include <Window.h>
32 
33 #include <keyboard_mouse_driver.h>
34 
35 
36 const uint32 SCROLL_X_DRAG = 'sxdr';
37 const uint32 SCROLL_Y_DRAG = 'sydr';
38 
39 #undef B_TRANSLATION_CONTEXT
40 #define B_TRANSLATION_CONTEXT "TouchpadPrefView"
41 
42 
43 TouchpadView::TouchpadView(BRect frame)
44 	:
45 	BView(frame, "TouchpadView", B_FOLLOW_NONE, B_WILL_DRAW)
46 {
47 	fXTracking = false;
48 	fYTracking = false;
49 	fOffScreenView = NULL;
50 	fOffScreenBitmap = NULL;
51 
52 	fPrefRect = frame;
53 	fPadRect = fPrefRect;
54 	fPadRect.InsetBy(10, 10);
55 	fXScrollRange = fPadRect.Width();
56 	fYScrollRange = fPadRect.Height();
57 }
58 
59 
60 TouchpadView::~TouchpadView()
61 {
62 	delete fOffScreenBitmap;
63 }
64 
65 
66 void
67 TouchpadView::Draw(BRect updateRect)
68 {
69 	DrawSliders();
70 }
71 
72 
73 void
74 TouchpadView::MouseDown(BPoint point)
75 {
76 	if (fXScrollDragZone.Contains(point)) {
77 		fXTracking = true;
78 		fOldXScrollRange = fXScrollRange;
79 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
80 	}
81 
82 	if (fYScrollDragZone.Contains(point)) {
83 		fYTracking = true;
84 		fOldYScrollRange = fYScrollRange;
85 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
86 	}
87 }
88 
89 
90 void
91 TouchpadView::MouseUp(BPoint point)
92 {
93 	if (!fXTracking && !fYTracking)
94 		return;
95 
96 	fXTracking = false;
97 	fYTracking = false;
98 
99 	const float kSoftScrollLimit = 0.7;
100 
101 	int32 result = 0;
102 	if (GetRightScrollRatio() > kSoftScrollLimit
103 		|| GetBottomScrollRatio() > kSoftScrollLimit) {
104 		BAlert* alert = new BAlert(B_TRANSLATE("Please confirm"),
105 			B_TRANSLATE(
106 				"The new scroll area is very large and may impede "
107 				"normal mouse operation. Do you really want to change it?"),
108 			B_TRANSLATE("OK"), B_TRANSLATE("Cancel"), NULL, B_WIDTH_AS_USUAL,
109 			B_WARNING_ALERT);
110 		alert->SetShortcut(1, B_ESCAPE);
111 		result = alert->Go();
112 	}
113 
114 	if (result == 0) {
115 		BMessage msg(SCROLL_AREA_CHANGED);
116 		Invoke(&msg);
117 	} else {
118 		if (GetRightScrollRatio() > kSoftScrollLimit)
119 			fXScrollRange = fOldXScrollRange;
120 		if (GetBottomScrollRatio() > kSoftScrollLimit)
121 			fYScrollRange = fOldYScrollRange;
122 		DrawSliders();
123 	}
124 }
125 
126 
127 void
128 TouchpadView::AttachedToWindow()
129 {
130 	if (!fOffScreenView)
131 		fOffScreenView = new BView(Bounds(), "", B_FOLLOW_ALL, B_WILL_DRAW);
132 
133 	if (!fOffScreenBitmap) {
134 		fOffScreenBitmap = new BBitmap(Bounds(), B_CMAP8, true, false);
135 
136 		if (fOffScreenBitmap && fOffScreenView)
137 			fOffScreenBitmap->AddChild(fOffScreenView);
138 	}
139 }
140 
141 
142 void
143 TouchpadView::SetValues(float rightRange, float bottomRange)
144 {
145 	fXScrollRange = fPadRect.Width() * (1 - rightRange);
146 	fYScrollRange = fPadRect.Height() * (1 - bottomRange);
147 	Invalidate();
148 }
149 
150 
151 void
152 TouchpadView::GetPreferredSize(float* width, float* height)
153 {
154 	if (width != NULL)
155 		*width = fPrefRect.Width();
156 	if (height != NULL)
157 		*height = fPrefRect.Height();
158 }
159 
160 
161 void
162 TouchpadView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
163 {
164 	if (fXTracking) {
165 		if (point.x > fPadRect.right)
166 			fXScrollRange = fPadRect.Width();
167 		else if (point.x < fPadRect.left)
168 			fXScrollRange = 0;
169 		else
170 			fXScrollRange = point.x - fPadRect.left;
171 
172 		DrawSliders();
173 	}
174 
175 	if (fYTracking) {
176 		if (point.y > fPadRect.bottom)
177 			fYScrollRange = fPadRect.Height();
178 		else if (point.y < fPadRect.top)
179 			fYScrollRange = 0;
180 		else
181 			fYScrollRange = point.y - fPadRect.top;
182 
183 		DrawSliders();
184 	}
185 }
186 
187 
188 void
189 TouchpadView::DrawSliders()
190 {
191 	BView* view = fOffScreenView != NULL ? fOffScreenView : this;
192 
193 	if (!LockLooper())
194 		return;
195 
196 	if (fOffScreenBitmap->Lock()) {
197 		view->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
198 		view->FillRect(Bounds());
199 		view->SetHighColor(100, 100, 100);
200 		view->FillRoundRect(fPadRect, 4, 4);
201 
202 		int32 dragSize = 3; // half drag size
203 
204 		// scroll areas
205 		view->SetHighColor(145, 100, 100);
206 		BRect rightRect(fPadRect.left + fXScrollRange, fPadRect.top,
207 			fPadRect.right, fPadRect.bottom);
208 		view->FillRoundRect(rightRect, 4, 4);
209 
210 		BRect bottomRect(fPadRect.left, fPadRect.top + fYScrollRange,
211 			fPadRect.right, fPadRect.bottom);
212 		view->FillRoundRect(bottomRect, 4, 4);
213 
214 		// Stroke Rect
215 		view->SetHighColor(100, 100, 100);
216 		view->SetPenSize(2);
217 		view->StrokeRoundRect(fPadRect, 4, 4);
218 
219 		// x scroll range line
220 		view->SetHighColor(200, 0, 0);
221 		view->StrokeLine(BPoint(fPadRect.left + fXScrollRange, fPadRect.top),
222 			BPoint(fPadRect.left + fXScrollRange, fPadRect.bottom));
223 
224 		fXScrollDragZone = BRect(fPadRect.left + fXScrollRange - dragSize,
225 			fPadRect.top - dragSize, fPadRect.left + fXScrollRange + dragSize,
226 			fPadRect.bottom + dragSize);
227 		BRect xscrollDragZone1 = BRect(fPadRect.left + fXScrollRange - dragSize,
228 			fPadRect.top - dragSize, fPadRect.left + fXScrollRange + dragSize,
229 			fPadRect.top + dragSize);
230 		view->FillRect(xscrollDragZone1);
231 		BRect xscrollDragZone2 = BRect(fPadRect.left + fXScrollRange - dragSize,
232 			fPadRect.bottom - dragSize,
233 			fPadRect.left + fXScrollRange + dragSize,
234 			fPadRect.bottom + dragSize);
235 		view->FillRect(xscrollDragZone2);
236 
237 		// y scroll range line
238 		view->StrokeLine(BPoint(fPadRect.left, fPadRect.top + fYScrollRange),
239 			BPoint(fPadRect.right, fPadRect.top + fYScrollRange));
240 
241 		fYScrollDragZone = BRect(fPadRect.left - dragSize,
242 			fPadRect.top + fYScrollRange - dragSize, fPadRect.right + dragSize,
243 			fPadRect.top + fYScrollRange + dragSize);
244 		BRect yscrollDragZone1 = BRect(fPadRect.left - dragSize,
245 			fPadRect.top + fYScrollRange - dragSize, fPadRect.left + dragSize,
246 			fPadRect.top + fYScrollRange + dragSize);
247 		view->FillRect(yscrollDragZone1);
248 		BRect yscrollDragZone2 = BRect(fPadRect.right - dragSize,
249 			fPadRect.top + fYScrollRange - dragSize, fPadRect.right + dragSize,
250 			fPadRect.top + fYScrollRange + dragSize);
251 		view->FillRect(yscrollDragZone2);
252 
253 		view->Sync();
254 		fOffScreenBitmap->Unlock();
255 		DrawBitmap(fOffScreenBitmap, B_ORIGIN);
256 	}
257 
258 	UnlockLooper();
259 }
260 
261 
262 //	#pragma mark - TouchpadPrefView
263 
264 
265 TouchpadPrefView::TouchpadPrefView(BInputDevice* dev)
266 	:
267 	BGroupView(),
268 	fTouchpadPref(dev)
269 {
270 	SetupView();
271 	// set view values
272 	SetValues(&fTouchpadPref.Settings());
273 }
274 
275 
276 TouchpadPrefView::~TouchpadPrefView()
277 {
278 }
279 
280 
281 void
282 TouchpadPrefView::MessageReceived(BMessage* message)
283 {
284 	touchpad_settings& settings = fTouchpadPref.Settings();
285 
286 	switch (message->what) {
287 		case SCROLL_AREA_CHANGED:
288 			settings.scroll_rightrange = fTouchpadView->GetRightScrollRatio();
289 			settings.scroll_bottomrange = fTouchpadView->GetBottomScrollRatio();
290 			fTouchpadPref.UpdateSettings();
291 			break;
292 
293 		case SCROLL_CONTROL_CHANGED:
294 			settings.scroll_twofinger = fTwoFingerBox->Value() == B_CONTROL_ON;
295 			settings.scroll_twofinger_horizontal
296 				= fTwoFingerHorizontalBox->Value() == B_CONTROL_ON;
297 			settings.scroll_acceleration = fScrollAccelSlider->Value();
298 			settings.scroll_xstepsize = (20 - fScrollStepXSlider->Value()) * 3;
299 			settings.scroll_ystepsize = (20 - fScrollStepYSlider->Value()) * 3;
300 			fTwoFingerHorizontalBox->SetEnabled(settings.scroll_twofinger);
301 			fTouchpadPref.UpdateSettings();
302 			break;
303 
304 		case TAP_CONTROL_CHANGED:
305 			settings.tapgesture_sensibility = fTapSlider->Value();
306 			fTouchpadPref.UpdateSettings();
307 			break;
308 
309 		case PADBLOCK_TIME_CHANGED:
310 			settings.padblocker_threshold = fPadBlockerSlider->Value();
311 			// The maximum value means "disabled", but in the settings file that
312 			// must be stored as 0
313 			if (settings.padblocker_threshold == 1000)
314 				settings.padblocker_threshold = 0;
315 			fRevertButton->SetEnabled(true);
316 			fTouchpadPref.UpdateSettings();
317 			break;
318 
319 		case DEFAULT_SETTINGS:
320 			fTouchpadPref.Defaults();
321 			fRevertButton->SetEnabled(true);
322 			fTouchpadPref.UpdateSettings();
323 			SetValues(&settings);
324 			break;
325 
326 		case REVERT_SETTINGS:
327 			fTouchpadPref.Revert();
328 			fTouchpadPref.UpdateSettings();
329 			fRevertButton->SetEnabled(false);
330 			SetValues(&settings);
331 			break;
332 
333 		default:
334 			BView::MessageReceived(message);
335 	}
336 }
337 
338 
339 void
340 TouchpadPrefView::AttachedToWindow()
341 {
342 	fTouchpadView->SetTarget(this);
343 	fTwoFingerBox->SetTarget(this);
344 	fTwoFingerHorizontalBox->SetTarget(this);
345 	fScrollStepXSlider->SetTarget(this);
346 	fScrollStepYSlider->SetTarget(this);
347 	fScrollAccelSlider->SetTarget(this);
348 	fPadBlockerSlider->SetTarget(this);
349 	fTapSlider->SetTarget(this);
350 	fDefaultButton->SetTarget(this);
351 	fRevertButton->SetTarget(this);
352 	BSize size = PreferredSize();
353 	Window()->ResizeTo(size.width, size.height);
354 
355 	BPoint position = fTouchpadPref.WindowPosition();
356 	// center window on screen if it had a bad position
357 	if (position.x < 0 && position.y < 0)
358 		Window()->CenterOnScreen();
359 	else
360 		Window()->MoveTo(position);
361 }
362 
363 
364 void
365 TouchpadPrefView::DetachedFromWindow()
366 {
367 	fTouchpadPref.SetWindowPosition(Window()->Frame().LeftTop());
368 }
369 
370 
371 void
372 TouchpadPrefView::SetupView()
373 {
374 	SetLayout(new BGroupLayout(B_VERTICAL));
375 	BBox* scrollBox = new BBox("Touchpad");
376 	scrollBox->SetLabel(B_TRANSLATE("Scrolling"));
377 
378 
379 	fTouchpadView = new TouchpadView(BRect(0, 0, 130, 120));
380 	fTouchpadView->SetExplicitMaxSize(BSize(130, 120));
381 
382 	// Create the "Mouse Speed" slider...
383 	fScrollAccelSlider = new BSlider("scroll_accel",
384 		B_TRANSLATE("Acceleration"),
385 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
386 	fScrollAccelSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
387 	fScrollAccelSlider->SetHashMarkCount(7);
388 	fScrollAccelSlider->SetLimitLabels(
389 		B_TRANSLATE("Slow"), B_TRANSLATE("Fast"));
390 	fScrollAccelSlider->SetExplicitMinSize(BSize(150, B_SIZE_UNSET));
391 
392 	fScrollStepXSlider = new BSlider("scroll_stepX", B_TRANSLATE("Horizontal"),
393 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
394 	fScrollStepXSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
395 	fScrollStepXSlider->SetHashMarkCount(7);
396 	fScrollStepXSlider->SetLimitLabels(
397 		B_TRANSLATE("Slow"), B_TRANSLATE("Fast"));
398 
399 	fScrollStepYSlider = new BSlider("scroll_stepY", B_TRANSLATE("Vertical"),
400 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
401 	fScrollStepYSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
402 	fScrollStepYSlider->SetHashMarkCount(7);
403 	fScrollStepYSlider->SetLimitLabels(
404 		B_TRANSLATE("Slow"), B_TRANSLATE("Fast"));
405 
406 	fPadBlockerSlider
407 		= new BSlider("padblocker", B_TRANSLATE("Keyboard lock delay"),
408 			new BMessage(PADBLOCK_TIME_CHANGED), 5, 1000, B_HORIZONTAL);
409 	fPadBlockerSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
410 	fPadBlockerSlider->SetHashMarkCount(10);
411 	fPadBlockerSlider->SetLimitLabels(
412 		B_TRANSLATE("Quick"), B_TRANSLATE("Never"));
413 
414 	fTwoFingerBox = new BCheckBox(B_TRANSLATE("Two finger scrolling"),
415 		new BMessage(SCROLL_CONTROL_CHANGED));
416 	fTwoFingerHorizontalBox = new BCheckBox(B_TRANSLATE("Horizontal scrolling"),
417 		new BMessage(SCROLL_CONTROL_CHANGED));
418 
419 	float spacing = be_control_look->DefaultItemSpacing();
420 
421 	BView* scrollPrefLeftLayout
422 		= BLayoutBuilder::Group<>(B_VERTICAL, 0)
423 		.Add(fTouchpadView)
424 		.AddStrut(spacing)
425 		.Add(fTwoFingerBox)
426 		.AddGroup(B_HORIZONTAL, 0)
427 			.AddStrut(spacing * 2)
428 			.Add(fTwoFingerHorizontalBox)
429 			.End()
430 		.AddGlue()
431 		.View();
432 
433 	BGroupView* scrollPrefRightLayout = new BGroupView(B_VERTICAL);
434 	scrollPrefRightLayout->AddChild(fScrollAccelSlider);
435 	scrollPrefRightLayout->AddChild(fScrollStepXSlider);
436 	scrollPrefRightLayout->AddChild(fScrollStepYSlider);
437 
438 	BGroupLayout* scrollPrefLayout = new BGroupLayout(B_HORIZONTAL);
439 	scrollPrefLayout->SetSpacing(spacing);
440 	scrollPrefLayout->SetInsets(
441 		spacing, scrollBox->TopBorderOffset() * 2 + spacing, spacing, spacing);
442 	scrollBox->SetLayout(scrollPrefLayout);
443 
444 	scrollPrefLayout->AddView(scrollPrefLeftLayout);
445 	scrollPrefLayout->AddItem(
446 		BSpaceLayoutItem::CreateVerticalStrut(spacing * 1.5));
447 	scrollPrefLayout->AddView(scrollPrefRightLayout);
448 
449 	fTapSlider = new BSlider("tap_sens", B_TRANSLATE("Tapping sensitivity"),
450 		new BMessage(TAP_CONTROL_CHANGED), 0, spacing * 2, B_HORIZONTAL);
451 	fTapSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
452 	fTapSlider->SetHashMarkCount(7);
453 	fTapSlider->SetLimitLabels(B_TRANSLATE("Off"), B_TRANSLATE("High"));
454 
455 	fDefaultButton
456 		= new BButton(B_TRANSLATE("Defaults"), new BMessage(DEFAULT_SETTINGS));
457 
458 	fRevertButton
459 		= new BButton(B_TRANSLATE("Revert"), new BMessage(REVERT_SETTINGS));
460 	fRevertButton->SetEnabled(false);
461 
462 
463 	BLayoutBuilder::Group<>(this, B_VERTICAL)
464 		.SetInsets(B_USE_WINDOW_SPACING)
465 		.Add(scrollBox)
466 		.Add(fTapSlider)
467 		.Add(fPadBlockerSlider)
468 		.Add(new BSeparatorView(B_HORIZONTAL))
469 			.AddGroup(B_HORIZONTAL)
470 			.Add(fDefaultButton)
471 			.Add(fRevertButton)
472 			.AddGlue()
473 		.End()
474 	.End();
475 }
476 
477 
478 void
479 TouchpadPrefView::SetValues(touchpad_settings* settings)
480 {
481 	fTouchpadView->SetValues(
482 		settings->scroll_rightrange, settings->scroll_bottomrange);
483 	fTwoFingerBox->SetValue(
484 		settings->scroll_twofinger ? B_CONTROL_ON : B_CONTROL_OFF);
485 	fTwoFingerHorizontalBox->SetValue(
486 		settings->scroll_twofinger_horizontal ? B_CONTROL_ON : B_CONTROL_OFF);
487 	fTwoFingerHorizontalBox->SetEnabled(settings->scroll_twofinger);
488 	fScrollStepXSlider->SetValue(20 - settings->scroll_xstepsize / 2);
489 	fScrollStepYSlider->SetValue(20 - settings->scroll_ystepsize / 2);
490 	fScrollAccelSlider->SetValue(settings->scroll_acceleration);
491 	fTapSlider->SetValue(settings->tapgesture_sensibility);
492 	fPadBlockerSlider->SetValue(settings->padblocker_threshold);
493 }
494