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