xref: /haiku/src/preferences/input/InputTouchpadPrefView.cpp (revision 9295c1f645806eca5d7699c985f7b509528c9eaa)
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 
61 TouchpadView::~TouchpadView()
62 {
63 	delete fOffScreenBitmap;
64 }
65 
66 
67 void
68 TouchpadView::Draw(BRect updateRect)
69 {
70 	DrawSliders();
71 }
72 
73 
74 void
75 TouchpadView::MouseDown(BPoint point)
76 {
77 	if (fXScrollDragZone.Contains(point)) {
78 		fXTracking = true;
79 		fOldXScrollRange = fXScrollRange;
80 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
81 	}
82 
83 	if (fYScrollDragZone.Contains(point)) {
84 		fYTracking = true;
85 		fOldYScrollRange = fYScrollRange;
86 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
87 	}
88 }
89 
90 
91 void
92 TouchpadView::MouseUp(BPoint point)
93 {
94 	if (!fXTracking && !fYTracking)
95 		return;
96 
97 	fXTracking = false;
98 	fYTracking = false;
99 
100 	const float kSoftScrollLimit = 0.7;
101 
102 	int32 result = 0;
103 	if (GetRightScrollRatio() > kSoftScrollLimit
104 		|| GetBottomScrollRatio() > kSoftScrollLimit) {
105 		BAlert* alert = new BAlert(B_TRANSLATE("Please confirm"),
106 			B_TRANSLATE("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"),
109 			NULL, B_WIDTH_AS_USUAL, 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 
189 void
190 TouchpadView::DrawSliders()
191 {
192 	BView* view = fOffScreenView != NULL ? fOffScreenView : this;
193 
194 	if (!LockLooper())
195 		return;
196 
197 	if (fOffScreenBitmap->Lock()) {
198 		view->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
199 		view->FillRect(Bounds());
200 		view->SetHighColor(100, 100, 100);
201 		view->FillRoundRect(fPadRect, 4, 4);
202 
203 		int32 dragSize = 3; // half drag size
204 
205 		// scroll areas
206 		view->SetHighColor(145, 100, 100);
207 		BRect rightRect(fPadRect.left + fXScrollRange,  fPadRect.top,
208 			fPadRect.right, fPadRect.bottom);
209 		view->FillRoundRect(rightRect, 4, 4);
210 
211 		BRect bottomRect(fPadRect.left, fPadRect.top + fYScrollRange,
212 			fPadRect.right, fPadRect.bottom);
213 		view->FillRoundRect(bottomRect, 4, 4);
214 
215 		// Stroke Rect
216 		view->SetHighColor(100, 100, 100);
217 		view->SetPenSize(2);
218 		view->StrokeRoundRect(fPadRect, 4, 4);
219 
220 		// x scroll range line
221 		view->SetHighColor(200, 0, 0);
222 		view->StrokeLine(BPoint(fPadRect.left + fXScrollRange, fPadRect.top),
223 			BPoint(fPadRect.left + fXScrollRange, fPadRect.bottom));
224 
225 		fXScrollDragZone = BRect(fPadRect.left + fXScrollRange - dragSize,
226 			fPadRect.top - dragSize, fPadRect.left + fXScrollRange + dragSize,
227 			fPadRect.bottom + dragSize);
228 		BRect xscrollDragZone1 = BRect(fPadRect.left + fXScrollRange - dragSize,
229 			fPadRect.top - dragSize, fPadRect.left + fXScrollRange + dragSize,
230 			fPadRect.top + dragSize);
231 		view->FillRect(xscrollDragZone1);
232 		BRect xscrollDragZone2 = BRect(fPadRect.left + fXScrollRange - dragSize,
233 			fPadRect.bottom - dragSize,
234 			fPadRect.left + fXScrollRange + dragSize,
235 			fPadRect.bottom + dragSize);
236 		view->FillRect(xscrollDragZone2);
237 
238 		// y scroll range line
239 		view->StrokeLine(BPoint(fPadRect.left, fPadRect.top + fYScrollRange),
240 			BPoint(fPadRect.right, fPadRect.top  + fYScrollRange));
241 
242 		fYScrollDragZone = BRect(fPadRect.left - dragSize,
243 			fPadRect.top + fYScrollRange - dragSize,
244 			fPadRect.right  + dragSize,
245 			fPadRect.top + fYScrollRange + dragSize);
246 		BRect yscrollDragZone1 = BRect(fPadRect.left - dragSize,
247 			fPadRect.top + fYScrollRange - dragSize, fPadRect.left  + dragSize,
248 			fPadRect.top + fYScrollRange + dragSize);
249 		view->FillRect(yscrollDragZone1);
250 		BRect yscrollDragZone2 = BRect(fPadRect.right - dragSize,
251 			fPadRect.top + fYScrollRange - dragSize, fPadRect.right  + dragSize,
252 			fPadRect.top + fYScrollRange + dragSize);
253 		view->FillRect(yscrollDragZone2);
254 
255 		view->Sync();
256 		fOffScreenBitmap->Unlock();
257 		DrawBitmap(fOffScreenBitmap, B_ORIGIN);
258 	}
259 
260 	UnlockLooper();
261 }
262 
263 
264 //	#pragma mark - TouchpadPrefView
265 
266 
267 TouchpadPrefView::TouchpadPrefView(BInputDevice* dev)
268 	:
269 	BGroupView(),
270 	fTouchpadPref(dev)
271 {
272 	SetupView();
273 	// set view values
274 	SetValues(&fTouchpadPref.Settings());
275 }
276 
277 
278 TouchpadPrefView::~TouchpadPrefView()
279 {
280 }
281 
282 
283 void
284 TouchpadPrefView::MessageReceived(BMessage* message)
285 {
286 	touchpad_settings& settings = fTouchpadPref.Settings();
287 
288 	switch (message->what) {
289 		case SCROLL_AREA_CHANGED:
290 			settings.scroll_rightrange = fTouchpadView->GetRightScrollRatio();
291 			settings.scroll_bottomrange = fTouchpadView->GetBottomScrollRatio();
292 			fTouchpadPref.UpdateSettings();
293 			break;
294 
295 		case SCROLL_CONTROL_CHANGED:
296 			settings.scroll_twofinger = fTwoFingerBox->Value() == B_CONTROL_ON;
297 			settings.scroll_twofinger_horizontal
298 				= fTwoFingerHorizontalBox->Value() == B_CONTROL_ON;
299 			settings.scroll_acceleration = fScrollAccelSlider->Value();
300 			settings.scroll_xstepsize = (20 - fScrollStepXSlider->Value()) * 3;
301 			settings.scroll_ystepsize = (20 - fScrollStepYSlider->Value()) * 3;
302 			fTwoFingerHorizontalBox->SetEnabled(settings.scroll_twofinger);
303 			fTouchpadPref.UpdateSettings();
304 			break;
305 
306 		case TAP_CONTROL_CHANGED:
307 			settings.tapgesture_sensibility = fTapSlider->Value();
308 			fTouchpadPref.UpdateSettings();
309 			break;
310 
311 		case PADBLOCK_TIME_CHANGED:
312 			settings.padblocker_threshold = fPadBlockerSlider->Value();
313 			// The maximum value means "disabled", but in the settings file that
314 			// must be stored as 0
315 			if (settings.padblocker_threshold == 1000)
316 				settings.padblocker_threshold = 0;
317 			fRevertButton->SetEnabled(true);
318 			fTouchpadPref.UpdateSettings();
319 			break;
320 
321 		case DEFAULT_SETTINGS:
322 			fTouchpadPref.Defaults();
323 			fRevertButton->SetEnabled(true);
324 			fTouchpadPref.UpdateSettings();
325 			SetValues(&settings);
326 			break;
327 
328 		case REVERT_SETTINGS:
329 			fTouchpadPref.Revert();
330 			fTouchpadPref.UpdateSettings();
331 			fRevertButton->SetEnabled(false);
332 			SetValues(&settings);
333 			break;
334 
335 		default:
336 			BView::MessageReceived(message);
337 	}
338 }
339 
340 
341 void
342 TouchpadPrefView::AttachedToWindow()
343 {
344 	fTouchpadView->SetTarget(this);
345 	fTwoFingerBox->SetTarget(this);
346 	fTwoFingerHorizontalBox->SetTarget(this);
347 	fScrollStepXSlider->SetTarget(this);
348 	fScrollStepYSlider->SetTarget(this);
349 	fScrollAccelSlider->SetTarget(this);
350 	fPadBlockerSlider->SetTarget(this);
351 	fTapSlider->SetTarget(this);
352 	fDefaultButton->SetTarget(this);
353 	fRevertButton->SetTarget(this);
354 	BSize size = PreferredSize();
355 	Window()->ResizeTo(size.width, size.height);
356 
357 	BPoint position = fTouchpadPref.WindowPosition();
358 	// center window on screen if it had a bad position
359 	if (position.x < 0 && position.y < 0)
360 		Window()->CenterOnScreen();
361 	else
362 		Window()->MoveTo(position);
363 }
364 
365 
366 void
367 TouchpadPrefView::DetachedFromWindow()
368 {
369 	fTouchpadPref.SetWindowPosition(Window()->Frame().LeftTop());
370 }
371 
372 
373 void
374 TouchpadPrefView::SetupView()
375 {
376 	SetLayout(new BGroupLayout(B_VERTICAL));
377 	BBox* scrollBox = new BBox("Touchpad");
378 	scrollBox->SetLabel(B_TRANSLATE("Scrolling"));
379 
380 
381 	fTouchpadView = new TouchpadView(BRect(0, 0, 130, 120));
382 	fTouchpadView->SetExplicitMaxSize(BSize(130, 120));
383 
384 	// Create the "Mouse Speed" slider...
385 	fScrollAccelSlider = new BSlider("scroll_accel",
386 		B_TRANSLATE("Acceleration"),
387 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
388 	fScrollAccelSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
389 	fScrollAccelSlider->SetHashMarkCount(7);
390 	fScrollAccelSlider->SetLimitLabels(B_TRANSLATE("Slow"),
391 		B_TRANSLATE("Fast"));
392 	fScrollAccelSlider->SetExplicitMinSize(BSize(150, B_SIZE_UNSET));
393 
394 	fScrollStepXSlider = new BSlider("scroll_stepX",
395 		B_TRANSLATE("Horizontal"),
396 		new BMessage(SCROLL_CONTROL_CHANGED),
397 		0, 20, B_HORIZONTAL);
398 	fScrollStepXSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
399 	fScrollStepXSlider->SetHashMarkCount(7);
400 	fScrollStepXSlider->SetLimitLabels(B_TRANSLATE("Slow"),
401 		B_TRANSLATE("Fast"));
402 
403 	fScrollStepYSlider = new BSlider("scroll_stepY",
404 		B_TRANSLATE("Vertical"),
405 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
406 	fScrollStepYSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
407 	fScrollStepYSlider->SetHashMarkCount(7);
408 	fScrollStepYSlider->SetLimitLabels(B_TRANSLATE("Slow"),
409 		B_TRANSLATE("Fast"));
410 
411 	fPadBlockerSlider = new BSlider("padblocker",
412 		B_TRANSLATE("Keyboard Lock Delay"),
413 		new BMessage(PADBLOCK_TIME_CHANGED), 5, 1000, B_HORIZONTAL);
414 	fPadBlockerSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
415 	fPadBlockerSlider->SetHashMarkCount(10);
416 	fPadBlockerSlider->SetLimitLabels(B_TRANSLATE("Quick"),
417 		B_TRANSLATE("Never"));
418 
419 	fTwoFingerBox = new BCheckBox(B_TRANSLATE("Two finger scrolling"),
420 		new BMessage(SCROLL_CONTROL_CHANGED));
421 	fTwoFingerHorizontalBox = new BCheckBox(
422 		B_TRANSLATE("Horizontal scrolling"),
423 		new BMessage(SCROLL_CONTROL_CHANGED));
424 
425 	float spacing = be_control_look->DefaultItemSpacing();
426 
427 	BView* scrollPrefLeftLayout
428 		= BLayoutBuilder::Group<>(B_VERTICAL, 0)
429 		.Add(fTouchpadView)
430 		.AddStrut(spacing)
431 		.Add(fTwoFingerBox)
432 		.AddGroup(B_HORIZONTAL, 0)
433 			.AddStrut(spacing * 2)
434 			.Add(fTwoFingerHorizontalBox)
435 			.End()
436 		.AddGlue()
437 		.View();
438 
439 	BGroupView* scrollPrefRightLayout = new BGroupView(B_VERTICAL);
440 	scrollPrefRightLayout->AddChild(fScrollAccelSlider);
441 	scrollPrefRightLayout->AddChild(fScrollStepXSlider);
442 	scrollPrefRightLayout->AddChild(fScrollStepYSlider);
443 
444 	BGroupLayout* scrollPrefLayout = new BGroupLayout(B_HORIZONTAL);
445 	scrollPrefLayout->SetSpacing(spacing);
446 	scrollPrefLayout->SetInsets(spacing,
447 		scrollBox->TopBorderOffset() * 2 + spacing, spacing, spacing);
448 	scrollBox->SetLayout(scrollPrefLayout);
449 
450 	scrollPrefLayout->AddView(scrollPrefLeftLayout);
451 	scrollPrefLayout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(spacing
452 		* 1.5));
453 	scrollPrefLayout->AddView(scrollPrefRightLayout);
454 
455 	fTapSlider = new BSlider("tap_sens", B_TRANSLATE("Tapping sensitivity"),
456 		new BMessage(TAP_CONTROL_CHANGED), 0, spacing * 2, B_HORIZONTAL);
457 	fTapSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
458 	fTapSlider->SetHashMarkCount(7);
459 	fTapSlider->SetLimitLabels(B_TRANSLATE("Off"), B_TRANSLATE("High"));
460 
461 	fDefaultButton = new BButton(B_TRANSLATE("Defaults"),
462 		new BMessage(DEFAULT_SETTINGS));
463 
464 	fRevertButton = new BButton(B_TRANSLATE("Revert"),
465 		new BMessage(REVERT_SETTINGS));
466 	fRevertButton->SetEnabled(false);
467 
468 
469 	BLayoutBuilder::Group<>(this, B_VERTICAL)
470 		.SetInsets(B_USE_WINDOW_SPACING)
471 		.Add(scrollBox)
472 		.Add(fTapSlider)
473 		.Add(fPadBlockerSlider)
474 		.Add(new BSeparatorView(B_HORIZONTAL))
475 			.AddGroup(B_HORIZONTAL)
476 			.Add(fDefaultButton)
477 			.Add(fRevertButton)
478 			.AddGlue()
479 		.End()
480 	.End();
481 }
482 
483 
484 void
485 TouchpadPrefView::SetValues(touchpad_settings* settings)
486 {
487 	fTouchpadView->SetValues(settings->scroll_rightrange,
488 		settings->scroll_bottomrange);
489 	fTwoFingerBox->SetValue(settings->scroll_twofinger
490 		? B_CONTROL_ON : B_CONTROL_OFF);
491 	fTwoFingerHorizontalBox->SetValue(settings->scroll_twofinger_horizontal
492 		? B_CONTROL_ON : B_CONTROL_OFF);
493 	fTwoFingerHorizontalBox->SetEnabled(settings->scroll_twofinger);
494 	fScrollStepXSlider->SetValue(20 - settings->scroll_xstepsize / 2);
495 	fScrollStepYSlider->SetValue(20 - settings->scroll_ystepsize / 2);
496 	fScrollAccelSlider->SetValue(settings->scroll_acceleration);
497 	fTapSlider->SetValue(settings->tapgesture_sensibility);
498 	fPadBlockerSlider->SetValue(settings->padblocker_threshold);
499 }
500