BColorMenuItem: add custom color menufield

http://insightfactory.tumblr.com/image/142366356207

* Make the color box a rectangle with proportions of golden ratio.
* Override GetContentSize() to make menu item area larger.
* Label should never truncate since I make sure there is enough room.
* Draw the label using BMenuItem parent class
* Carefully adjust the spacing so that there is an attractive amount of
  padding between the checkmark and color box and the color box and label.

Add _AddMenu method to BMenuField that adds BColorMenuItem as its
base menu item. This shows the BColorMenuItem in the closed state.

Create BPrivate::MenuItemPrivate

Add a SetSubmenu() method to MenuItemPrivate that gives you
the ability to add a submenu after creating the object. This
method should be public

Skip disabled items

Color gets updated even if you select an item in a submenu
This commit is contained in:
John Scipione
2016-07-31 19:42:39 -07:00
parent ca766a50fe
commit 81364c9d82
8 changed files with 472 additions and 11 deletions
+3 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2015, Haiku, Inc. All rights reserved.
* Copyright 2006-2016 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MENU_FIELD_H
@@ -135,6 +135,7 @@ private:
void _InitMenuBar(BMenu* menu,
BRect frame, bool fixedSize);
void _InitMenuBar(const BMessage* archive);
void _AddMenu(BMenu* menu);
void _ValidateLayoutData();
float _MenuBarOffset() const;
@@ -159,4 +160,5 @@ private:
uint32 _reserved[2];
};
#endif // _MENU_FIELD_H
+6
View File
@@ -15,6 +15,10 @@
class BMessage;
class BWindow;
namespace BPrivate {
class MenuItemPrivate;
}
class BMenuItem : public BArchivable, public BInvoker {
public:
BMenuItem(const char* label, BMessage* message,
@@ -57,6 +61,7 @@ private:
friend class BMenu;
friend class BPopUpMenu;
friend class BMenuBar;
friend class BPrivate::MenuItemPrivate;
virtual void _ReservedMenuItem1();
virtual void _ReservedMenuItem2();
@@ -77,6 +82,7 @@ private:
BMenuItem(const BMenuItem& other);
BMenuItem& operator=(const BMenuItem& other);
private:
void _InitData();
void _InitMenuData(BMenu* menu);
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright 2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, [email protected]
*/
#ifndef COLOR_MENU_ITEM_H
#define COLOR_MENU_ITEM_H
#include <InterfaceDefs.h>
#include <MenuItem.h>
class BColorMenuItem : public BMenuItem {
public:
BColorMenuItem(const char* label,
BMessage* message, rgb_color color,
char shortcut = 0,
uint32 modifiers = 0);
BColorMenuItem(BMenu* menu, rgb_color color,
BMessage* message = NULL);
BColorMenuItem(BMessage* data);
static BArchivable* Instantiate(BMessage* archive);
virtual status_t Archive(BMessage* archive,
bool deep = true) const;
virtual void DrawContent();
virtual void GetContentSize(float* _width, float* _height);
virtual void SetMarked(bool mark);
rgb_color Color() const { return fColor; };
virtual void SetColor(rgb_color color) { fColor = color; };
private:
virtual void _ReservedColorMenuItem1();
virtual void _ReservedColorMenuItem2();
virtual void _ReservedColorMenuItem3();
virtual void _ReservedColorMenuItem4();
virtual void _ReservedColorMenuItem5();
virtual void _ReservedColorMenuItem6();
virtual void _ReservedColorMenuItem7();
virtual void _ReservedColorMenuItem8();
virtual void _ReservedColorMenuItem9();
virtual void _ReservedColorMenuItem10();
virtual void _ReservedColorMenuItem11();
virtual void _ReservedColorMenuItem12();
virtual void _ReservedColorMenuItem13();
virtual void _ReservedColorMenuItem14();
virtual void _ReservedColorMenuItem15();
virtual void _ReservedColorMenuItem16();
virtual void _ReservedColorMenuItem17();
virtual void _ReservedColorMenuItem18();
virtual void _ReservedColorMenuItem19();
virtual void _ReservedColorMenuItem20();
float _LeftMargin();
float _Padding();
float _ColorRectWidth();
private:
rgb_color fColor;
uint32 _reserved[30];
};
#endif // COLOR_MENU_ITEM_H
@@ -0,0 +1,35 @@
/*
* Copyright 2016 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, [email protected]
*/
#ifndef __MENU_ITEM_PRIVATE_H
#define __MENU_ITEM_PRIVATE_H
#include <MenuItem.h>
class BMenu;
namespace BPrivate {
class MenuItemPrivate {
public:
MenuItemPrivate(BMenuItem* menuItem);
void SetSubmenu(BMenu* submenu);
void Install(BWindow* window);
void Uninstall();
private:
BMenuItem* fMenuItem;
};
}; // namespace BPrivate
#endif // __MENU_ITEM_PRIVATE_H
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright 2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, [email protected]
*/
#include "ColorMenuItem.h"
#include <math.h>
#include <string.h>
#include <syslog.h>
#include <algorithm>
#include <MenuField.h>
#include <MenuPrivate.h>
// golden ratio
#ifdef M_PHI
# undef M_PHI
#endif
#define M_PHI 1.61803398874989484820
// #pragma - ColorMenuItem
BColorMenuItem::BColorMenuItem(const char* label, BMessage* message,
rgb_color color, char shortcut, uint32 modifiers)
:
BMenuItem(label, message, shortcut, modifiers),
fColor(color)
{
}
BColorMenuItem::BColorMenuItem(BMenu* menu, rgb_color color,
BMessage* message)
:
BMenuItem(menu, message),
fColor(color)
{
}
BColorMenuItem::BColorMenuItem(BMessage* data)
:
BMenuItem(data)
{
if (data->HasColor("_color")) {
rgb_color color;
color = data->GetColor("_color", (rgb_color){ 0, 0, 0 });
fColor = color;
} else
fColor = (rgb_color){ 0, 0, 0 };
}
BArchivable*
BColorMenuItem::Instantiate(BMessage* data)
{
if (validate_instantiation(data, "BColorMenuItem"))
return new BColorMenuItem(data);
return NULL;
}
status_t
BColorMenuItem::Archive(BMessage* data, bool deep) const
{
status_t result = BMenuItem::Archive(data, deep);
if (result == B_OK)
result = data->AddColor("_color", fColor);
return result;
}
void
BColorMenuItem::DrawContent()
{
float leftMargin = _LeftMargin();
float padding = _Padding();
float colorRectWidth = _ColorRectWidth();
BRect colorRect(Frame().InsetByCopy(2.0f, 2.0f));
colorRect.left = colorRect.right = leftMargin;
colorRect.right += colorRectWidth;
rgb_color highColor = Menu()->HighColor();
Menu()->SetDrawingMode(B_OP_OVER);
Menu()->SetHighColor(fColor);
Menu()->FillRect(colorRect);
Menu()->SetHighColor(ui_color(B_CONTROL_BORDER_COLOR));
Menu()->StrokeRect(colorRect);
float width = colorRectWidth + padding;
Menu()->MovePenBy(width, 0.0f);
Menu()->SetHighColor(highColor);
BMenuItem::DrawContent();
}
void
BColorMenuItem::GetContentSize(float* _width, float* _height)
{
float labelWidth;
float height;
BMenuItem::GetContentSize(&labelWidth, &height);
if (_width != NULL)
*_width = _LeftMargin() + _ColorRectWidth() + _Padding() + labelWidth;
if (_height != NULL)
*_height = height;
}
void
BColorMenuItem::SetMarked(bool mark)
{
BMenuItem::SetMarked(mark);
if (!mark)
return;
// we are marking the item
BMenu* menu = Menu();
if (menu == NULL)
return;
// we have a parent menu
BMenu* _menu = menu;
while ((_menu = _menu->Supermenu()) != NULL)
menu = _menu;
// went up the hierarchy to found the topmost menu
if (menu == NULL || menu->Parent() == NULL)
return;
// our topmost menu has a parent
if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL)
return;
// our topmost menu's parent is a BMenuField
BMenuItem* topLevelItem = menu->ItemAt((int32)0);
if (topLevelItem == NULL)
return;
// our topmost menu has a menu item
BColorMenuItem* topLevelColorMenuItem
= dynamic_cast<BColorMenuItem*>(topLevelItem);
if (topLevelColorMenuItem == NULL)
return;
// our topmost menu's item is a BColorMenuItem
// update the color
topLevelColorMenuItem->SetColor(fColor);
menu->Invalidate();
}
// #pragma mark - BColorMenuItem private methods
float
BColorMenuItem::_LeftMargin()
{
BPrivate::MenuPrivate menuPrivate(Menu());
float leftMargin;
menuPrivate.GetItemMargins(&leftMargin, NULL, NULL, NULL);
return leftMargin;
}
float
BColorMenuItem::_Padding()
{
return floorf(std::max(14.0f, be_plain_font->Size() + 2) / 2);
}
float
BColorMenuItem::_ColorRectWidth()
{
return floorf(std::max(14.0f, be_plain_font->Size() + 2) * M_PHI);
}
// #pragma mark - BColorMenuItem reserved virtual methods
void BColorMenuItem::_ReservedColorMenuItem1() {}
void BColorMenuItem::_ReservedColorMenuItem2() {}
void BColorMenuItem::_ReservedColorMenuItem3() {}
void BColorMenuItem::_ReservedColorMenuItem4() {}
void BColorMenuItem::_ReservedColorMenuItem5() {}
void BColorMenuItem::_ReservedColorMenuItem6() {}
void BColorMenuItem::_ReservedColorMenuItem7() {}
void BColorMenuItem::_ReservedColorMenuItem8() {}
void BColorMenuItem::_ReservedColorMenuItem9() {}
void BColorMenuItem::_ReservedColorMenuItem10() {}
void BColorMenuItem::_ReservedColorMenuItem11() {}
void BColorMenuItem::_ReservedColorMenuItem12() {}
void BColorMenuItem::_ReservedColorMenuItem13() {}
void BColorMenuItem::_ReservedColorMenuItem14() {}
void BColorMenuItem::_ReservedColorMenuItem15() {}
void BColorMenuItem::_ReservedColorMenuItem16() {}
void BColorMenuItem::_ReservedColorMenuItem17() {}
void BColorMenuItem::_ReservedColorMenuItem18() {}
void BColorMenuItem::_ReservedColorMenuItem19() {}
void BColorMenuItem::_ReservedColorMenuItem20() {}
+2
View File
@@ -57,6 +57,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
CheckBox.cpp
ColorConversion.cpp
ColorControl.cpp
ColorMenuItem.cpp
Control.cpp
ControlLook.cpp
DecorInfo.cpp
@@ -90,6 +91,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
MenuBar.cpp
MenuField.cpp
MenuItem.cpp
MenuItemPrivate.cpp
MenuPrivate.cpp
MenuWindow.cpp
OptionControl.cpp
+66 -10
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2001-2013, Haiku, Inc.
* Copyright 2006-2016 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -13,17 +13,20 @@
#include <MenuField.h>
#include <algorithm>
#include <stdio.h>
// for printf in TRACE
#include <stdlib.h>
#include <string.h>
#include <AbstractLayoutItem.h>
#include <Archivable.h>
#include <BMCPrivate.h>
#include <ControlLook.h>
#include <LayoutUtils.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <MenuItemPrivate.h>
#include <MenuPrivate.h>
#include <Message.h>
#include <MessageFilter.h>
@@ -194,8 +197,6 @@ MouseDownFilter::Filter(BMessage* message, BHandler** target)
// #pragma mark - BMenuField
using BPrivate::MenuPrivate;
BMenuField::BMenuField(BRect frame, const char* name, const char* label,
BMenu* menu, uint32 resizingMode, uint32 flags)
:
@@ -300,6 +301,7 @@ BMenuField::BMenuField(BMessage* data)
if (!BUnarchiver::IsArchiveManaged(data))
_InitMenuBar(data);
unarchiver.Finish();
}
@@ -1081,7 +1083,7 @@ BMenuField::_DrawLabel(BRect updateRect)
PushState();
rgb_color textColor;
MenuPrivate menuPrivate(fMenuBar);
BPrivate::MenuPrivate menuPrivate(fMenuBar);
if (menuPrivate.State() != MENU_STATE_CLOSED) {
// highlight the background of the label grey (like BeOS R5)
SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR));
@@ -1207,9 +1209,6 @@ BMenuField::_InitMenuBar(BMenu* menu, BRect frame, bool fixedSize)
{
CALLED();
fMenu = menu;
InitMenu(menu);
if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
fMenuBar = new _BMCMenuBar_(this);
} else {
@@ -1236,7 +1235,9 @@ BMenuField::_InitMenuBar(BMenu* menu, BRect frame, bool fixedSize)
}
AddChild(fMenuBar);
fMenuBar->AddItem(menu);
_AddMenu(menu);
fMenuBar->SetFont(be_plain_font);
}
@@ -1257,7 +1258,7 @@ BMenuField::_InitMenuBar(const BMessage* archive)
// this is normally done in InitObject2()
}
fMenu = fMenuBar->SubmenuAt(0);
_AddMenu(fMenuBar->SubmenuAt(0));
bool disable;
if (archive->FindBool("_disable", &disable) == B_OK)
@@ -1271,6 +1272,62 @@ BMenuField::_InitMenuBar(const BMessage* archive)
}
void
BMenuField::_AddMenu(BMenu* menu)
{
if (menu == NULL || fMenuBar == NULL)
return;
fMenu = menu;
InitMenu(menu);
BMenuItem* item = NULL;
if (!menu->IsRadioMode() || (item = menu->FindMarked()) == NULL) {
// find the first enabled non-seperator item
int32 itemCount = menu->CountItems();
for (int32 i = 0; i < itemCount; i++) {
item = menu->ItemAt((int32)i);
if (item == NULL || !item->IsEnabled()
|| dynamic_cast<BSeparatorItem*>(item) != NULL) {
item = NULL;
continue;
}
break;
}
}
if (item == NULL) {
fMenuBar->AddItem(menu);
return;
}
// build an empty copy of item
BMessage data;
status_t result = item->Archive(&data, false);
if (result != B_OK) {
fMenuBar->AddItem(menu);
return;
}
BArchivable* object = instantiate_object(&data);
if (object == NULL) {
fMenuBar->AddItem(menu);
return;
}
BMenuItem* newItem = static_cast<BMenuItem*>(object);
// unset parameters
BPrivate::MenuItemPrivate newMenuItemPrivate(newItem);
newMenuItemPrivate.Uninstall();
// set the menu
newMenuItemPrivate.SetSubmenu(menu);
fMenuBar->AddItem(newItem);
}
void
BMenuField::_ValidateLayoutData()
{
@@ -1630,4 +1687,3 @@ B_IF_GCC_2(InvalidateLayout__10BMenuFieldb, _ZN10BMenuField16InvalidateLayoutEb)
field->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
}
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2016 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#include <MenuItemPrivate.h>
#include <Menu.h>
namespace BPrivate {
MenuItemPrivate::MenuItemPrivate(BMenuItem* menuItem)
:
fMenuItem(menuItem)
{
}
void
MenuItemPrivate::SetSubmenu(BMenu* submenu)
{
delete fMenuItem->fSubmenu;
fMenuItem->_InitMenuData(submenu);
if (fMenuItem->fSuper != NULL) {
fMenuItem->fSuper->InvalidateLayout();
if (fMenuItem->fSuper->LockLooper()) {
fMenuItem->fSuper->Invalidate();
fMenuItem->fSuper->UnlockLooper();
}
}
}
void
MenuItemPrivate::Install(BWindow* window)
{
fMenuItem->Install(window);
}
void
MenuItemPrivate::Uninstall()
{
fMenuItem->Uninstall();
}
} // namespace BPrivate