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