First try at menu scrolling, courthesy of Łukasz Zemczak. Works more or less, although a bit buggy

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19343 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stefano Ceccherini 2006-11-20 20:19:55 +00:00
parent 1941200400
commit 27cc25083e
3 changed files with 237 additions and 33 deletions

View File

@ -30,7 +30,44 @@
#include <Window.h>
class BMenu;
class BMenuScroller;
class BMenuScroller : public BView {
public:
BMenuScroller(BRect frame, BMenu *menu);
virtual ~BMenuScroller();
virtual void Pulse();
virtual void Draw(BRect updateRect);
private:
BMenu *fMenu;
BRect fUpperButton;
BRect fLowerButton;
float fValue;
float fLimit;
bool fUpperEnabled;
bool fLowerEnabled;
uint32 fButton;
BPoint fPosition;
};
class BMenuFrame : public BView {
public:
BMenuFrame(BMenu *menu);
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect);
private:
friend class BMenuWindow;
BMenu *fMenu;
};
class BMenuWindow : public BWindow {
public:
@ -40,11 +77,12 @@ public:
void AttachMenu(BMenu *menu);
void DetachMenu();
void UpdateScrollers();
void AttachScrollers();
void DetachScrollers();
private:
BMenuScroller *fUpperScroller;
BMenuScroller *fLowerScroller;
BMenuScroller *fScroller;
BMenuFrame *fMenuFrame;
};
#endif

View File

@ -1184,6 +1184,7 @@ BMenu::_show(bool selectFirstItem)
if (window->Lock()) {
fAttachAborted = false;
window->AttachMenu(this);
fCachedMenuWindow = window;
// Menu didn't have the time to add its items: aborting...
if (fAttachAborted) {
@ -1203,6 +1204,7 @@ BMenu::_show(bool selectFirstItem)
MoveTo(1, 1);
UpdateWindowViewSize();
fCachedMenuWindow = NULL;
window->Show();
if (selectFirstItem)
@ -2061,13 +2063,27 @@ BMenu::UpdateWindowViewSize(bool upWind)
BRect frame = CalcFrame(ScreenLocation(), &scroll);
ResizeTo(frame.Width(), frame.Height());
if (fItems.CountItems() > 0)
if (fItems.CountItems() > 0) {
if (!scroll) {
window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
else {
} else {
BScreen screen(window);
// If we need scrolling, resize the window to fit the screen and
// attach scrollers to our cached MenuWindow.
window->ResizeTo(Bounds().Width() + 2, screen.Frame().bottom - 10);
if (fCachedMenuWindow)
fCachedMenuWindow->AttachScrollers();
frame.top = 0;
}
} else {
CacheFontInfo();
window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right,
fFontHeight + fPad.top + fPad.bottom);
}
window->MoveTo(frame.LeftTop());
}

View File

@ -5,12 +5,11 @@
* Authors:
* Marc Flerackers (mflerackers@androme.be)
* Stefano Ceccherini (burton666@libero.it)
*
*/
//! BMenuWindow is a custom BWindow for BMenus.
// TODO: Add scrollers
#include <Menu.h>
#include <MenuPrivate.h>
@ -18,27 +17,133 @@
#include <WindowPrivate.h>
// This draws the frame around the BMenu
class BMenuFrame : public BView {
public:
BMenuFrame(BMenu *menu);
const int kScrollerHeight = 10;
const int kScrollStep = 16;
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect);
BMenuScroller::BMenuScroller(BRect frame, BMenu *menu)
: BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS
| B_PULSE_NEEDED),
fMenu(menu),
fUpperButton(0, 0, frame.right, kScrollerHeight),
fLowerButton(0, frame.bottom - kScrollerHeight, frame.right, frame.bottom),
fValue(0),
fUpperEnabled(false),
fLowerEnabled(true)
{
if (!menu)
debugger("BMenuScroller(): Scroller not attached to a menu!");
SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
private:
BMenu *fMenu;
};
fLimit = menu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight);
}
BMenuScroller::~BMenuScroller()
{
}
void
BMenuScroller::Pulse()
{
// To reduce the overhead even a little, fButton and fPosition were
// made private variables.
// We track the mouse and move the scrollers depending on its position.
GetMouse(&fPosition, &fButton);
if (fLowerButton.Contains(fPosition) && fLowerEnabled) {
if (fValue == 0) {
fUpperEnabled = true;
Invalidate(fUpperButton);
}
if (fValue + kScrollStep >= fLimit) {
// If we reached the limit, we don't want to scroll a whole
// 'step' if not needed.
fMenu->ScrollBy(0, fLimit - fValue);
fValue = fLimit;
fLowerEnabled = false;
Invalidate(fLowerButton);
} else {
fMenu->ScrollBy(0, kScrollStep);
fValue += kScrollStep;
}
} else if (fUpperButton.Contains(fPosition) && fUpperEnabled) {
if (fValue == fLimit) {
fLowerEnabled = true;
Invalidate(fLowerButton);
}
if (fValue - kScrollStep <= 0) {
fMenu->ScrollBy(0, -fValue);
fValue = 0;
fUpperEnabled = false;
Invalidate(fUpperButton);
} else {
fMenu->ScrollBy(0, -kScrollStep);
fValue -= kScrollStep;
}
// In this case, we need to redraw the lower button because of a
// probable bug in ScrollBy handling. The scrolled view is drawing below
// its bounds, dirtying our button in this process.
// Redrawing it everytime makes scrolling a little slower.
// TODO: Try to find why and fix this.
Draw(fLowerButton);
}
}
void
BMenuScroller::Draw(BRect updateRect)
{
SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
float middle = Bounds().right / 2;
// Draw the upper arrow.
if (updateRect.Intersects(fUpperButton)) {
if (fUpperEnabled)
SetHighColor(0, 0, 0);
else
SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
B_DARKEN_2_TINT));
FillRect(fUpperButton, B_SOLID_LOW);
FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3),
BPoint(middle + 5, (kScrollerHeight / 2) + 2),
BPoint(middle - 5, (kScrollerHeight / 2) + 2));
}
// Draw the lower arrow.
if (updateRect.Intersects(fLowerButton)) {
if (fLowerEnabled)
SetHighColor(0, 0, 0);
else
SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
B_DARKEN_2_TINT));
FillRect(fLowerButton, B_SOLID_LOW);
FillTriangle(BPoint(middle, fLowerButton.bottom - (kScrollerHeight / 2) + 3),
BPoint(middle + 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2),
BPoint(middle - 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2));
}
}
// #pragma mark -
BMenuWindow::BMenuWindow(const char *name)
// The window will be resized by BMenu, so just pass a dummy rect
: BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
B_NOT_ZOOMABLE | B_AVOID_FOCUS),
fUpperScroller(NULL),
fLowerScroller(NULL)
fScroller(NULL),
fMenuFrame(NULL)
{
SetPulseRate(200000);
}
@ -50,11 +155,11 @@ BMenuWindow::~BMenuWindow()
void
BMenuWindow::AttachMenu(BMenu *menu)
{
if (CountChildren() > 0)
debugger("BMenuWindow (%s): a menu is already attached!");
if (fMenuFrame)
debugger("BMenuWindow: a menu is already attached!");
if (menu != NULL) {
BMenuFrame *menuFrame = new BMenuFrame(menu);
AddChild(menuFrame);
fMenuFrame = new BMenuFrame(menu);
AddChild(fMenuFrame);
menu->MakeFocus(true);
}
}
@ -63,8 +168,53 @@ BMenuWindow::AttachMenu(BMenu *menu)
void
BMenuWindow::DetachMenu()
{
if (CountChildren() > 0)
RemoveChild(ChildAt(0));
if (fMenuFrame) {
if (fScroller) {
DetachScrollers();
} else {
RemoveChild(fMenuFrame);
}
delete fMenuFrame;
fMenuFrame = NULL;
}
}
void
BMenuWindow::AttachScrollers()
{
// We want to attach a scroller only if there's a menu frame already
// existing.
if (fScroller || !fMenuFrame)
return;
RemoveChild(fMenuFrame);
fScroller = new BMenuScroller(Bounds(), fMenuFrame->fMenu);
fScroller->AddChild(fMenuFrame);
AddChild(fScroller);
fMenuFrame->fMenu->MakeFocus(true);
fMenuFrame->ResizeBy(0, -2 * kScrollerHeight);
fMenuFrame->MoveBy(0, kScrollerHeight);
}
void
BMenuWindow::DetachScrollers()
{
if(!fScroller || !fMenuFrame)
return;
// BeOS doesn't remember the position where the last scrolling ended,
// so we just scroll back to the beginning.
fMenuFrame->fMenu->ScrollTo(0, 0);
fScroller->RemoveChild(fMenuFrame);
RemoveChild(fScroller);
delete fScroller;
fScroller = NULL;
}