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