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 "MouseView.h" 11 12 #include <algorithm> 13 14 #include <Box.h> 15 #include <Button.h> 16 #include <Debug.h> 17 #include <GradientLinear.h> 18 #include <GradientRadial.h> 19 #include <MenuField.h> 20 #include <MenuItem.h> 21 #include <PopUpMenu.h> 22 #include <Region.h> 23 #include <Shape.h> 24 #include <Slider.h> 25 #include <TextControl.h> 26 #include <TranslationUtils.h> 27 #include <TranslatorFormats.h> 28 #include <Window.h> 29 30 #include "InputConstants.h" 31 #include "MouseSettings.h" 32 #include "InputWindow.h" 33 34 35 static const int32 kButtonTop = 3; 36 static const int32 kMouseDownWidth = 72; 37 static const int32 kMouseDownHeight = 35; 38 39 static const int32 kOneButtonOffsets[4] 40 = { 0, kMouseDownWidth }; 41 static const int32 kTwoButtonOffsets[4] 42 = { 0, kMouseDownWidth / 2, kMouseDownWidth }; 43 static const int32 kThreeButtonOffsets[4] 44 = { 0, kMouseDownWidth / 3 + 1, kMouseDownWidth / 3 * 2, kMouseDownWidth }; 45 46 static const rgb_color kButtonTextColor = { 0, 0, 0, 255 }; 47 static const rgb_color kMouseShadowColor = { 100, 100, 100, 128 }; 48 static const rgb_color kMouseBodyTopColor = { 0xed, 0xed, 0xed, 255 }; 49 static const rgb_color kMouseBodyBottomColor = { 0x85, 0x85, 0x85, 255 }; 50 static const rgb_color kMouseOutlineColor = { 0x51, 0x51, 0x51, 255 }; 51 static const rgb_color kMouseButtonOutlineColor = { 0xa0, 0xa0, 0xa0, 255 }; 52 static const rgb_color kButtonPressedColor = { 110, 110, 110, 110 }; 53 54 55 static const int32* 56 getButtonOffsets(int32 type) 57 { 58 switch (type) { 59 case 1: 60 return kOneButtonOffsets; 61 case 2: 62 return kTwoButtonOffsets; 63 case 3: 64 default: 65 return kThreeButtonOffsets; 66 } 67 } 68 69 70 static uint32 71 getMappingNumber(int32 mapping) 72 { 73 switch (mapping) { 74 case B_PRIMARY_MOUSE_BUTTON: 75 return 0; 76 case B_SECONDARY_MOUSE_BUTTON: 77 return 1; 78 case B_TERTIARY_MOUSE_BUTTON: 79 return 2; 80 } 81 return 0; 82 } 83 84 MouseView::MouseView(const MouseSettings &settings) 85 : 86 BView("Mouse", B_PULSE_NEEDED | B_WILL_DRAW), 87 fSettings(settings), 88 fType(-1), 89 fButtons(0), 90 fOldButtons(0) 91 { 92 SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 93 fScaling = std::max(1.0f, be_plain_font->Size() / 12.0f); 94 } 95 96 97 MouseView::~MouseView() 98 { 99 } 100 101 102 void 103 MouseView::SetMouseType(int32 type) 104 { 105 fType = type; 106 Invalidate(); 107 } 108 109 110 void 111 MouseView::MouseMapUpdated() 112 { 113 Invalidate(); 114 } 115 116 117 void 118 MouseView::UpdateFromSettings() 119 { 120 SetMouseType(fSettings.MouseType()); 121 } 122 123 124 void 125 MouseView::GetPreferredSize(float* _width, float* _height) 126 { 127 if (_width != NULL) 128 *_width = fScaling * (kMouseDownWidth + 2); 129 if (_height != NULL) 130 *_height = fScaling * 104; 131 } 132 133 134 void 135 MouseView::AttachedToWindow() 136 { 137 AdoptParentColors(); 138 139 UpdateFromSettings(); 140 _CreateButtonsPicture(); 141 142 font_height fontHeight; 143 GetFontHeight(&fontHeight); 144 fDigitHeight = int32(ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)); 145 fDigitBaseline = int32(ceilf(fontHeight.ascent)); 146 } 147 148 149 void 150 MouseView::MouseUp(BPoint) 151 { 152 fButtons = 0; 153 Invalidate(_ButtonsRect()); 154 fOldButtons = fButtons; 155 } 156 157 158 void 159 MouseView::MouseDown(BPoint where) 160 { 161 BMessage *mouseMsg = Window()->CurrentMessage(); 162 fButtons = mouseMsg->FindInt32("buttons"); 163 int32 modifiers = mouseMsg->FindInt32("modifiers"); 164 if (modifiers & B_CONTROL_KEY) { 165 if (modifiers & B_COMMAND_KEY) 166 fButtons = B_TERTIARY_MOUSE_BUTTON; 167 else 168 fButtons = B_SECONDARY_MOUSE_BUTTON; 169 } 170 171 // Get the current clipping region before requesting any updates. 172 // Otherwise those parts would be excluded from the region. 173 BRegion clipping; 174 GetClippingRegion(&clipping); 175 176 if (fOldButtons != fButtons) { 177 Invalidate(_ButtonsRect()); 178 fOldButtons = fButtons; 179 } 180 181 const int32* offset = getButtonOffsets(fType); 182 int32 button = -1; 183 for (int32 i = 0; i <= fType; i++) { 184 if (_ButtonRect(offset, i).Contains(where)) { 185 button = i; 186 break; 187 } 188 } 189 if (button < 0) 190 return; 191 192 if (clipping.Contains(where)) { 193 button = _ConvertFromVisualOrder(button); 194 195 BPopUpMenu menu("Mouse Map Menu"); 196 BMessage message(kMsgMouseMap); 197 message.AddInt32("button", button); 198 199 menu.AddItem(new BMenuItem("1", new BMessage(message))); 200 menu.AddItem(new BMenuItem("2", new BMessage(message))); 201 menu.AddItem(new BMenuItem("3", new BMessage(message))); 202 203 menu.ItemAt(getMappingNumber(fSettings.Mapping(button))) 204 ->SetMarked(true); 205 menu.SetTargetForItems(Window()); 206 207 ConvertToScreen(&where); 208 menu.Go(where, true); 209 } 210 } 211 212 213 void 214 MouseView::Draw(BRect updateFrame) 215 { 216 SetDrawingMode(B_OP_ALPHA); 217 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 218 SetScale(fScaling * 1.8); 219 220 BShape mouseShape; 221 mouseShape.MoveTo(BPoint(16, 12)); 222 // left 223 BPoint control[3] = { BPoint(12, 16), BPoint(8, 64), BPoint(32, 64) }; 224 mouseShape.BezierTo(control); 225 // right 226 BPoint control2[3] = { BPoint(56, 64), BPoint(52, 16), BPoint(48, 12) }; 227 mouseShape.BezierTo(control2); 228 // top 229 BPoint control3[3] = { BPoint(44, 8), BPoint(20, 8), BPoint(16, 12) }; 230 mouseShape.BezierTo(control3); 231 mouseShape.Close(); 232 233 // Draw the shadow 234 SetOrigin(-17 * fScaling, -11 * fScaling); 235 SetHighColor(kMouseShadowColor); 236 FillShape(&mouseShape, B_SOLID_HIGH); 237 238 // Draw the body 239 SetOrigin(-21 * fScaling, -14 * fScaling); 240 BGradientRadial bodyGradient(28, 24, 128); 241 bodyGradient.AddColor(kMouseBodyTopColor, 0); 242 bodyGradient.AddColor(kMouseBodyBottomColor, 255); 243 244 FillShape(&mouseShape, bodyGradient); 245 246 // Draw the outline 247 SetPenSize(1 / 1.8 / fScaling); 248 SetDrawingMode(B_OP_OVER); 249 SetHighColor(kMouseOutlineColor); 250 251 StrokeShape(&mouseShape, B_SOLID_HIGH); 252 253 // bottom button border 254 BShape buttonsOutline; 255 buttonsOutline.MoveTo(BPoint(13, 27)); 256 BPoint control4[] = { BPoint(18, 30), BPoint(46, 30), BPoint(51, 27) }; 257 buttonsOutline.BezierTo(control4); 258 259 SetHighColor(kMouseButtonOutlineColor); 260 StrokeShape(&buttonsOutline, B_SOLID_HIGH); 261 262 SetScale(1); 263 SetOrigin(0, 0); 264 265 // Separator between the buttons 266 const int32* offset = getButtonOffsets(fType); 267 for (int32 i = 1; i < fType; i++) { 268 BRect buttonRect = _ButtonRect(offset, i); 269 StrokeLine(buttonRect.LeftTop(), buttonRect.LeftBottom()); 270 } 271 272 mouse_map map; 273 fSettings.Mapping(map); 274 275 SetDrawingMode(B_OP_OVER); 276 277 if (fButtons != 0) 278 ClipToPicture(&fButtonsPicture, B_ORIGIN, false); 279 280 for (int32 i = 0; i < fType; i++) { 281 // draw mapping number centered over the button 282 283 bool pressed = (fButtons & map.button[_ConvertFromVisualOrder(i)]) != 0; 284 // is button currently pressed? 285 if (pressed) { 286 SetDrawingMode(B_OP_ALPHA); 287 SetHighColor(kButtonPressedColor); 288 FillRect(_ButtonRect(offset, i)); 289 } 290 291 BRect border(fScaling * (offset[i] + 1), fScaling * (kButtonTop + 5), 292 fScaling * offset[i + 1] - 1, 293 fScaling * (kButtonTop + kMouseDownHeight - 4)); 294 if (i == 0) 295 border.left += fScaling * 5; 296 if (i == fType - 1) 297 border.right -= fScaling * 4; 298 299 char number[2] = {0}; 300 number[0] = getMappingNumber(map.button[_ConvertFromVisualOrder(i)]) 301 + '1'; 302 303 SetDrawingMode(B_OP_OVER); 304 SetHighColor(kButtonTextColor); 305 DrawString(number, BPoint( 306 border.left + (border.Width() - StringWidth(number)) / 2, 307 border.top + fDigitBaseline 308 + (border.IntegerHeight() - fDigitHeight) / 2)); 309 } 310 311 if (fButtons != 0) 312 ClipToPicture(NULL); 313 } 314 315 316 BRect 317 MouseView::_ButtonsRect() const 318 { 319 return BRect(0, fScaling * kButtonTop, fScaling * kMouseDownWidth, 320 fScaling * (kButtonTop + kMouseDownHeight)); 321 } 322 323 324 BRect 325 MouseView::_ButtonRect(const int32* offsets, int index) const 326 { 327 return BRect(fScaling * offsets[index], fScaling * kButtonTop, 328 fScaling * offsets[index + 1] - 1, 329 fScaling * (kButtonTop + kMouseDownHeight)); 330 } 331 332 333 int32 334 MouseView::_ConvertFromVisualOrder(int32 i) 335 { 336 if (fType < 3) 337 return i; 338 339 switch (i) { 340 case 0: 341 default: 342 return 0; 343 case 1: 344 return 2; 345 case 2: 346 return 1; 347 } 348 } 349 350 351 void 352 MouseView::_CreateButtonsPicture() 353 { 354 BeginPicture(&fButtonsPicture); 355 SetScale(1.8 * fScaling); 356 SetOrigin(-21 * fScaling, -14 * fScaling); 357 358 BShape mouseShape; 359 mouseShape.MoveTo(BPoint(48, 12)); 360 // top 361 BPoint control3[3] = { BPoint(44, 8), BPoint(20, 8), BPoint(16, 12) }; 362 mouseShape.BezierTo(control3); 363 // left 364 BPoint control[3] = { BPoint(12, 16), BPoint(13, 27), BPoint(13, 27) }; 365 mouseShape.BezierTo(control); 366 // bottom 367 BPoint control4[3] = { BPoint(18, 30), BPoint(46, 30), BPoint(51, 27) }; 368 mouseShape.BezierTo(control4); 369 // right 370 BPoint control2[3] = { BPoint(51, 27), BPoint(50, 14), BPoint(48, 12) }; 371 mouseShape.BezierTo(control2); 372 373 mouseShape.Close(); 374 375 SetHighColor(255, 0, 0, 255); 376 FillShape(&mouseShape, B_SOLID_HIGH); 377 378 EndPicture(); 379 SetScale(1); 380 }