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 }