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
TouchpadView(BRect frame)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
~TouchpadView()60 TouchpadView::~TouchpadView()
61 {
62 delete fOffScreenBitmap;
63 }
64
65
66 void
Draw(BRect updateRect)67 TouchpadView::Draw(BRect updateRect)
68 {
69 DrawSliders();
70 }
71
72
73 void
MouseDown(BPoint point)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
MouseUp(BPoint point)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
AttachedToWindow()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
SetValues(float rightRange,float bottomRange)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
GetPreferredSize(float * width,float * height)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
MouseMoved(BPoint point,uint32 transit,const BMessage * message)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
DrawSliders()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
TouchpadPrefView(BInputDevice * dev)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
~TouchpadPrefView()276 TouchpadPrefView::~TouchpadPrefView()
277 {
278 }
279
280
281 void
MessageReceived(BMessage * message)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
AttachedToWindow()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
DetachedFromWindow()365 TouchpadPrefView::DetachedFromWindow()
366 {
367 fTouchpadPref.SetWindowPosition(Window()->Frame().LeftTop());
368 }
369
370
371 void
SetupView()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
SetValues(touchpad_settings * settings)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