xref: /haiku/src/preferences/input/InputTouchpadPrefView.cpp (revision 9e15c9f153c5ffff9ad51b95b581326eb579b0fd)
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 {
271 	SetupView();
272 	// set view values
273 	SetValues(&fTouchpadPref.Settings());
274 }
275 
276 
277 TouchpadPrefView::~TouchpadPrefView()
278 {
279 }
280 
281 
282 void
283 TouchpadPrefView::MessageReceived(BMessage* message)
284 {
285 	touchpad_settings& settings = fTouchpadPref.Settings();
286 
287 	switch (message->what) {
288 		case SCROLL_AREA_CHANGED:
289 			settings.scroll_rightrange = fTouchpadView->GetRightScrollRatio();
290 			settings.scroll_bottomrange = fTouchpadView->GetBottomScrollRatio();
291 			fTouchpadPref.UpdateSettings();
292 			break;
293 
294 		case SCROLL_CONTROL_CHANGED:
295 			settings.scroll_twofinger = fTwoFingerBox->Value() == B_CONTROL_ON;
296 			settings.scroll_twofinger_horizontal
297 				= fTwoFingerHorizontalBox->Value() == B_CONTROL_ON;
298 			settings.scroll_acceleration = fScrollAccelSlider->Value();
299 			settings.scroll_xstepsize = (20 - fScrollStepXSlider->Value()) * 3;
300 			settings.scroll_ystepsize = (20 - fScrollStepYSlider->Value()) * 3;
301 			fTwoFingerHorizontalBox->SetEnabled(settings.scroll_twofinger);
302 			fTouchpadPref.UpdateSettings();
303 			break;
304 
305 		case TAP_CONTROL_CHANGED:
306 			settings.tapgesture_sensibility = fTapSlider->Value();
307 			fTouchpadPref.UpdateSettings();
308 			break;
309 
310 		case DEFAULT_SETTINGS:
311 			fTouchpadPref.Defaults();
312 			fRevertButton->SetEnabled(true);
313 			fTouchpadPref.UpdateSettings();
314 			SetValues(&settings);
315 			break;
316 
317 		case REVERT_SETTINGS:
318 			fTouchpadPref.Revert();
319 			fTouchpadPref.UpdateSettings();
320 			fRevertButton->SetEnabled(false);
321 			SetValues(&settings);
322 			break;
323 
324 		default:
325 			BView::MessageReceived(message);
326 	}
327 }
328 
329 
330 void
331 TouchpadPrefView::AttachedToWindow()
332 {
333 	fTouchpadView->SetTarget(this);
334 	fTwoFingerBox->SetTarget(this);
335 	fTwoFingerHorizontalBox->SetTarget(this);
336 	fScrollStepXSlider->SetTarget(this);
337 	fScrollStepYSlider->SetTarget(this);
338 	fScrollAccelSlider->SetTarget(this);
339 	fTapSlider->SetTarget(this);
340 	fDefaultButton->SetTarget(this);
341 	fRevertButton->SetTarget(this);
342 	BSize size = PreferredSize();
343 	Window()->ResizeTo(size.width, size.height);
344 
345 	BPoint position = fTouchpadPref.WindowPosition();
346 	// center window on screen if it had a bad position
347 	if (position.x < 0 && position.y < 0)
348 		Window()->CenterOnScreen();
349 	else
350 		Window()->MoveTo(position);
351 }
352 
353 
354 void
355 TouchpadPrefView::DetachedFromWindow()
356 {
357 	fTouchpadPref.SetWindowPosition(Window()->Frame().LeftTop());
358 }
359 
360 
361 void
362 TouchpadPrefView::SetupView()
363 {
364 	SetLayout(new BGroupLayout(B_VERTICAL));
365 	BBox* scrollBox = new BBox("Touchpad");
366 	scrollBox->SetLabel(B_TRANSLATE("Scrolling"));
367 
368 
369 	fTouchpadView = new TouchpadView(BRect(0, 0, 130, 120));
370 	fTouchpadView->SetExplicitMaxSize(BSize(130, 120));
371 
372 	// Create the "Mouse Speed" slider...
373 	fScrollAccelSlider = new BSlider("scroll_accel",
374 		B_TRANSLATE("Acceleration"),
375 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
376 	fScrollAccelSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
377 	fScrollAccelSlider->SetHashMarkCount(7);
378 	fScrollAccelSlider->SetLimitLabels(B_TRANSLATE("Slow"),
379 		B_TRANSLATE("Fast"));
380 	fScrollAccelSlider->SetExplicitMinSize(BSize(150, B_SIZE_UNSET));
381 
382 	fScrollStepXSlider = new BSlider("scroll_stepX",
383 		B_TRANSLATE("Horizontal"),
384 		new BMessage(SCROLL_CONTROL_CHANGED),
385 		0, 20, B_HORIZONTAL);
386 	fScrollStepXSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
387 	fScrollStepXSlider->SetHashMarkCount(7);
388 	fScrollStepXSlider->SetLimitLabels(B_TRANSLATE("Slow"),
389 		B_TRANSLATE("Fast"));
390 
391 	fScrollStepYSlider = new BSlider("scroll_stepY",
392 		B_TRANSLATE("Vertical"),
393 		new BMessage(SCROLL_CONTROL_CHANGED), 0, 20, B_HORIZONTAL);
394 	fScrollStepYSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
395 	fScrollStepYSlider->SetHashMarkCount(7);
396 	fScrollStepYSlider->SetLimitLabels(B_TRANSLATE("Slow"),
397 		B_TRANSLATE("Fast"));
398 
399 	fTwoFingerBox = new BCheckBox(B_TRANSLATE("Two finger scrolling"),
400 		new BMessage(SCROLL_CONTROL_CHANGED));
401 	fTwoFingerHorizontalBox = new BCheckBox(
402 		B_TRANSLATE("Horizontal scrolling"),
403 		new BMessage(SCROLL_CONTROL_CHANGED));
404 
405 	float spacing = be_control_look->DefaultItemSpacing();
406 
407 	BView* scrollPrefLeftLayout
408 		= BLayoutBuilder::Group<>(B_VERTICAL, 0)
409 		.Add(fTouchpadView)
410 		.AddStrut(spacing)
411 		.Add(fTwoFingerBox)
412 		.AddGroup(B_HORIZONTAL, 0)
413 			.AddStrut(spacing * 2)
414 			.Add(fTwoFingerHorizontalBox)
415 			.End()
416 		.AddGlue()
417 		.View();
418 
419 	BGroupView* scrollPrefRightLayout = new BGroupView(B_VERTICAL);
420 	scrollPrefRightLayout->AddChild(fScrollAccelSlider);
421 	scrollPrefRightLayout->AddChild(fScrollStepXSlider);
422 	scrollPrefRightLayout->AddChild(fScrollStepYSlider);
423 
424 	BGroupLayout* scrollPrefLayout = new BGroupLayout(B_HORIZONTAL);
425 	scrollPrefLayout->SetSpacing(spacing);
426 	scrollPrefLayout->SetInsets(spacing,
427 		scrollBox->TopBorderOffset() * 2 + spacing, spacing, spacing);
428 	scrollBox->SetLayout(scrollPrefLayout);
429 
430 	scrollPrefLayout->AddView(scrollPrefLeftLayout);
431 	scrollPrefLayout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(spacing
432 		* 1.5));
433 	scrollPrefLayout->AddView(scrollPrefRightLayout);
434 
435 	BBox* tapBox = new BBox("tapbox");
436 	tapBox->SetLabel(B_TRANSLATE("Tapping"));
437 
438 	BGroupLayout* tapPrefLayout = new BGroupLayout(B_HORIZONTAL);
439 	tapPrefLayout->SetInsets(spacing, tapBox->TopBorderOffset() * 2 + spacing,
440 		spacing, spacing);
441 	tapBox->SetLayout(tapPrefLayout);
442 
443 	fTapSlider = new BSlider("tap_sens", B_TRANSLATE("Sensitivity"),
444 		new BMessage(TAP_CONTROL_CHANGED), 0, spacing * 2, B_HORIZONTAL);
445 	fTapSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
446 	fTapSlider->SetHashMarkCount(7);
447 	fTapSlider->SetLimitLabels(B_TRANSLATE("Off"), B_TRANSLATE("High"));
448 
449 	tapPrefLayout->AddView(fTapSlider);
450 
451 	fDefaultButton = new BButton(B_TRANSLATE("Defaults"),
452 		new BMessage(DEFAULT_SETTINGS));
453 
454 	fRevertButton = new BButton(B_TRANSLATE("Revert"),
455 		new BMessage(REVERT_SETTINGS));
456 	fRevertButton->SetEnabled(false);
457 
458 
459 	BLayoutBuilder::Group<>(this, B_VERTICAL)
460 		.SetInsets(B_USE_WINDOW_SPACING)
461 		.Add(scrollBox)
462 		.Add(tapBox)
463 			.Add(new BSeparatorView(B_HORIZONTAL))
464 				.AddGroup(B_HORIZONTAL)
465 				.Add(fDefaultButton)
466 				.Add(fRevertButton)
467 				.AddGlue()
468 				.End()
469 		.End();
470 }
471 
472 
473 void
474 TouchpadPrefView::SetValues(touchpad_settings* settings)
475 {
476 	fTouchpadView->SetValues(settings->scroll_rightrange,
477 		settings->scroll_bottomrange);
478 	fTwoFingerBox->SetValue(settings->scroll_twofinger
479 		? B_CONTROL_ON : B_CONTROL_OFF);
480 	fTwoFingerHorizontalBox->SetValue(settings->scroll_twofinger_horizontal
481 		? B_CONTROL_ON : B_CONTROL_OFF);
482 	fTwoFingerHorizontalBox->SetEnabled(settings->scroll_twofinger);
483 	fScrollStepXSlider->SetValue(20 - settings->scroll_xstepsize / 2);
484 	fScrollStepYSlider->SetValue(20 - settings->scroll_ystepsize / 2);
485 	fScrollAccelSlider->SetValue(settings->scroll_acceleration);
486 	fTapSlider->SetValue(settings->tapgesture_sensibility);
487 }