/* * Copyright 2019, Haiku, Inc. * Distributed under the terms of the MIT License. * * Author: * Preetpal Kaur */ #include "MouseView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "InputConstants.h" #include "MouseSettings.h" static const int32 kButtonTop = 3; static const int32 kMouseDownWidth = 72; static const int32 kMouseDownHeight = 35; #define W kMouseDownWidth / 100 static const int32 kButtonOffsets[][7] = { { 0, 100 * W }, { 0, 50 * W, 100 * W }, { 0, 35 * W, 65 * W, 100 * W }, { 0, 25 * W, 50 * W, 75 * W, 100 * W }, { 0, 20 * W, 40 * W, 60 * W, 80 * W, 100 * W }, { 0, 19 * W, 34 * W, 50 * W, 66 * W, 82 * W, 100 * W } }; #undef W static const rgb_color kButtonTextColor = { 0, 0, 0, 255 }; static const rgb_color kMouseShadowColor = { 100, 100, 100, 128 }; static const rgb_color kMouseBodyTopColor = { 0xed, 0xed, 0xed, 255 }; static const rgb_color kMouseBodyBottomColor = { 0x85, 0x85, 0x85, 255 }; static const rgb_color kMouseOutlineColor = { 0x51, 0x51, 0x51, 255 }; static const rgb_color kMouseButtonOutlineColor = { 0xa0, 0xa0, 0xa0, 255 }; static const rgb_color kButtonPressedColor = { 110, 110, 110, 110 }; static const int32* getButtonOffsets(int32 type) { if ((type - 1) >= (int32)B_COUNT_OF(kButtonOffsets)) return kButtonOffsets[2]; return kButtonOffsets[type - 1]; } static uint32 getMappingNumber(uint32 mapping) { if (mapping == 0) return 0; int i; for (i = 0; mapping != 1; i++) mapping >>= 1; return i; } MouseView::MouseView(const MouseSettings &settings) : BView("Mouse", B_PULSE_NEEDED | B_WILL_DRAW), fSettings(settings), fType(-1), fButtons(0), fOldButtons(0) { SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); fScaling = std::max(1.0f, be_plain_font->Size() / 7.0f); } MouseView::~MouseView() { } void MouseView::SetMouseType(int32 type) { fType = type; Invalidate(); } void MouseView::MouseMapUpdated() { Invalidate(); } void MouseView::UpdateFromSettings() { if (fSettings.MouseType() > 6) debugger("Mouse type is invalid"); SetMouseType(fSettings.MouseType()); } void MouseView::GetPreferredSize(float* _width, float* _height) { if (_width != NULL) *_width = fScaling * (kMouseDownWidth + 2); if (_height != NULL) *_height = fScaling * 104; } void MouseView::AttachedToWindow() { AdoptParentColors(); UpdateFromSettings(); _CreateButtonsPicture(); font_height fontHeight; GetFontHeight(&fontHeight); fDigitHeight = int32(ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)); fDigitBaseline = int32(ceilf(fontHeight.ascent)); } void MouseView::MouseUp(BPoint) { fButtons = 0; Invalidate(_ButtonsRect()); fOldButtons = fButtons; } void MouseView::MouseDown(BPoint where) { BMessage *mouseMsg = Window()->CurrentMessage(); fButtons = mouseMsg->FindInt32("buttons"); int32 modifiers = mouseMsg->FindInt32("modifiers"); if (modifiers & B_CONTROL_KEY) { if (modifiers & B_COMMAND_KEY) fButtons = B_TERTIARY_MOUSE_BUTTON; else fButtons = B_SECONDARY_MOUSE_BUTTON; } // Get the current clipping region before requesting any updates. // Otherwise those parts would be excluded from the region. BRegion clipping; GetClippingRegion(&clipping); if (fOldButtons != fButtons) { Invalidate(_ButtonsRect()); fOldButtons = fButtons; } const int32* offset = getButtonOffsets(fType); int32 button = -1; for (int32 i = 0; i <= fType; i++) { if (_ButtonRect(offset, i).Contains(where)) { button = i; break; } } if (button < 0) return; if (clipping.Contains(where)) { button = _ConvertFromVisualOrder(button); BPopUpMenu menu("Mouse Map Menu"); BMessage message(kMsgMouseMap); message.AddInt32("button", button); for (int i = 1; i < 7; i++) { char tmp[2]; sprintf(tmp, "%d", i); menu.AddItem(new BMenuItem(tmp, new BMessage(message))); } int32 mapping = fSettings.Mapping(button); BMenuItem* item = menu.ItemAt(getMappingNumber(mapping)); if (item) item->SetMarked(true); menu.SetTargetForItems(Window()); ConvertToScreen(&where); menu.Go(where, true); } } void MouseView::Draw(BRect updateFrame) { SetDrawingMode(B_OP_ALPHA); SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); SetScale(fScaling * 1.8); BShape mouseShape; mouseShape.MoveTo(BPoint(16, 12)); // left BPoint control[3] = { BPoint(12, 16), BPoint(8, 64), BPoint(32, 64) }; mouseShape.BezierTo(control); // right BPoint control2[3] = { BPoint(56, 64), BPoint(52, 16), BPoint(48, 12) }; mouseShape.BezierTo(control2); // top BPoint control3[3] = { BPoint(44, 8), BPoint(20, 8), BPoint(16, 12) }; mouseShape.BezierTo(control3); mouseShape.Close(); // Draw the shadow SetOrigin(-17 * fScaling, -11 * fScaling); SetHighColor(kMouseShadowColor); FillShape(&mouseShape, B_SOLID_HIGH); // Draw the body SetOrigin(-21 * fScaling, -14 * fScaling); BGradientRadial bodyGradient(28, 24, 128); bodyGradient.AddColor(kMouseBodyTopColor, 0); bodyGradient.AddColor(kMouseBodyBottomColor, 255); FillShape(&mouseShape, bodyGradient); // Draw the outline SetPenSize(1 / 1.8 / fScaling); SetDrawingMode(B_OP_OVER); SetHighColor(kMouseOutlineColor); StrokeShape(&mouseShape, B_SOLID_HIGH); // bottom button border BShape buttonsOutline; buttonsOutline.MoveTo(BPoint(13, 27)); BPoint control4[] = { BPoint(18, 30), BPoint(46, 30), BPoint(51, 27) }; buttonsOutline.BezierTo(control4); SetHighColor(kMouseButtonOutlineColor); StrokeShape(&buttonsOutline, B_SOLID_HIGH); SetScale(1); SetOrigin(0, 0); mouse_map map; fSettings.Mapping(map); SetDrawingMode(B_OP_OVER); // All button drawing is clipped to the outline of the buttons area, // simplifying the code below as it can overdraw things. ClipToPicture(&fButtonsPicture, B_ORIGIN, false); // Separator between the buttons const int32* offset = getButtonOffsets(fType); for (int32 i = 1; i < fType; i++) { BRect buttonRect = _ButtonRect(offset, i); StrokeLine(buttonRect.LeftTop(), buttonRect.LeftBottom()); } for (int32 i = 0; i < fType; i++) { // draw mapping number centered over the button bool pressed = (fButtons & map.button[_ConvertFromVisualOrder(i)]) != 0; // is button currently pressed? if (pressed) { SetDrawingMode(B_OP_ALPHA); SetHighColor(kButtonPressedColor); FillRect(_ButtonRect(offset, i)); } BRect border(fScaling * (offset[i] + 1), fScaling * (kButtonTop + 5), fScaling * offset[i + 1] - 1, fScaling * (kButtonTop + kMouseDownHeight - 4)); if (i == 0) border.left += fScaling * 5; if (i == fType - 1) border.right -= fScaling * 4; char label[2] = {0}; int32 number = getMappingNumber(map.button[_ConvertFromVisualOrder(i)]); label[0] = number + '1'; SetDrawingMode(B_OP_OVER); SetHighColor(kButtonTextColor); DrawString(label, BPoint( border.left + (border.Width() - StringWidth(label)) / 2, border.top + fDigitBaseline + (border.IntegerHeight() - fDigitHeight) / 2)); } ClipToPicture(NULL); } BRect MouseView::_ButtonsRect() const { return BRect(0, fScaling * kButtonTop, fScaling * kMouseDownWidth, fScaling * (kButtonTop + kMouseDownHeight)); } BRect MouseView::_ButtonRect(const int32* offsets, int index) const { return BRect(fScaling * offsets[index], fScaling * kButtonTop, fScaling * offsets[index + 1] - 1, fScaling * (kButtonTop + kMouseDownHeight)); } /** The buttons on a mouse are normally 1 (left), 2 (right), 3 (middle) so * we need to reorder them */ int32 MouseView::_ConvertFromVisualOrder(int32 i) { if (fType < 3) return i; switch (i) { case 0: return 0; case 1: return 2; case 2: return 1; default: return i; } } void MouseView::_CreateButtonsPicture() { BeginPicture(&fButtonsPicture); SetScale(1.8 * fScaling); SetOrigin(-21 * fScaling, -14 * fScaling); BShape mouseShape; mouseShape.MoveTo(BPoint(48, 12)); // top BPoint control3[3] = { BPoint(44, 8), BPoint(20, 8), BPoint(16, 12) }; mouseShape.BezierTo(control3); // left BPoint control[3] = { BPoint(12, 16), BPoint(13, 27), BPoint(13, 27) }; mouseShape.BezierTo(control); // bottom BPoint control4[3] = { BPoint(18, 30), BPoint(46, 30), BPoint(51, 27) }; mouseShape.BezierTo(control4); // right BPoint control2[3] = { BPoint(51, 27), BPoint(50, 14), BPoint(48, 12) }; mouseShape.BezierTo(control2); mouseShape.Close(); SetHighColor(255, 0, 0, 255); FillShape(&mouseShape, B_SOLID_HIGH); EndPicture(); SetOrigin(0, 0); SetScale(1); }