* Copyright 2019, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Author:
* Preetpal Kaur <preetpalok123@gmail.com>
*/
#include "MouseView.h"
#include <algorithm>
#include <Box.h>
#include <Button.h>
#include <Debug.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <Region.h>
#include <Shape.h>
#include <Slider.h>
#include <TextControl.h>
#include <TranslationUtils.h>
#include <TranslatorFormats.h>
#include <Window.h>
#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;
}
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));
BPoint control[3] = {BPoint(12, 16), BPoint(8, 64), BPoint(32, 64)};
mouseShape.BezierTo(control);
BPoint control2[3] = {BPoint(56, 64), BPoint(52, 16), BPoint(48, 12)};
mouseShape.BezierTo(control2);
BPoint control3[3] = {BPoint(44, 8), BPoint(20, 8), BPoint(16, 12)};
mouseShape.BezierTo(control3);
mouseShape.Close();
SetOrigin(-17 * fScaling, -11 * fScaling);
SetHighColor(kMouseShadowColor);
FillShape(&mouseShape, B_SOLID_HIGH);
SetOrigin(-21 * fScaling, -14 * fScaling);
BGradientRadial bodyGradient(28, 24, 128);
bodyGradient.AddColor(kMouseBodyTopColor, 0);
bodyGradient.AddColor(kMouseBodyBottomColor, 255);
FillShape(&mouseShape, bodyGradient);
SetPenSize(1 / 1.8 / fScaling);
SetDrawingMode(B_OP_OVER);
SetHighColor(kMouseOutlineColor);
StrokeShape(&mouseShape, B_SOLID_HIGH);
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);
ClipToPicture(&fButtonsPicture, B_ORIGIN, false);
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++) {
bool pressed = (fButtons & map.button[_ConvertFromVisualOrder(i)]) != 0;
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));
}
* 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));
BPoint control3[3] = {BPoint(44, 8), BPoint(20, 8), BPoint(16, 12)};
mouseShape.BezierTo(control3);
BPoint control[3] = {BPoint(12, 16), BPoint(13, 27), BPoint(13, 27)};
mouseShape.BezierTo(control);
BPoint control4[3] = {BPoint(18, 30), BPoint(46, 30), BPoint(51, 27)};
mouseShape.BezierTo(control4);
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);
}