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:
@@ -30,21 +30,59 @@
|
|||||||
#include <Window.h>
|
#include <Window.h>
|
||||||
|
|
||||||
class BMenu;
|
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 {
|
class BMenuWindow : public BWindow {
|
||||||
public:
|
public:
|
||||||
BMenuWindow(const char *name);
|
BMenuWindow(const char *name);
|
||||||
virtual ~BMenuWindow();
|
virtual ~BMenuWindow();
|
||||||
|
|
||||||
void AttachMenu(BMenu *menu);
|
void AttachMenu(BMenu *menu);
|
||||||
void DetachMenu();
|
void DetachMenu();
|
||||||
|
|
||||||
void UpdateScrollers();
|
void AttachScrollers();
|
||||||
|
void DetachScrollers();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BMenuScroller *fUpperScroller;
|
BMenuScroller *fScroller;
|
||||||
BMenuScroller *fLowerScroller;
|
BMenuFrame *fMenuFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1184,6 +1184,7 @@ BMenu::_show(bool selectFirstItem)
|
|||||||
if (window->Lock()) {
|
if (window->Lock()) {
|
||||||
fAttachAborted = false;
|
fAttachAborted = false;
|
||||||
window->AttachMenu(this);
|
window->AttachMenu(this);
|
||||||
|
fCachedMenuWindow = window;
|
||||||
|
|
||||||
// Menu didn't have the time to add its items: aborting...
|
// Menu didn't have the time to add its items: aborting...
|
||||||
if (fAttachAborted) {
|
if (fAttachAborted) {
|
||||||
@@ -1203,6 +1204,7 @@ BMenu::_show(bool selectFirstItem)
|
|||||||
MoveTo(1, 1);
|
MoveTo(1, 1);
|
||||||
|
|
||||||
UpdateWindowViewSize();
|
UpdateWindowViewSize();
|
||||||
|
fCachedMenuWindow = NULL;
|
||||||
window->Show();
|
window->Show();
|
||||||
|
|
||||||
if (selectFirstItem)
|
if (selectFirstItem)
|
||||||
@@ -2061,13 +2063,27 @@ BMenu::UpdateWindowViewSize(bool upWind)
|
|||||||
BRect frame = CalcFrame(ScreenLocation(), &scroll);
|
BRect frame = CalcFrame(ScreenLocation(), &scroll);
|
||||||
ResizeTo(frame.Width(), frame.Height());
|
ResizeTo(frame.Width(), frame.Height());
|
||||||
|
|
||||||
if (fItems.CountItems() > 0)
|
if (fItems.CountItems() > 0) {
|
||||||
window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
|
if (!scroll) {
|
||||||
else {
|
window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
|
||||||
|
} 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();
|
CacheFontInfo();
|
||||||
window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right,
|
window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right,
|
||||||
fFontHeight + fPad.top + fPad.bottom);
|
fFontHeight + fPad.top + fPad.bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
window->MoveTo(frame.LeftTop());
|
window->MoveTo(frame.LeftTop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,11 @@
|
|||||||
* Authors:
|
* Authors:
|
||||||
* Marc Flerackers ([email protected])
|
* Marc Flerackers ([email protected])
|
||||||
* Stefano Ceccherini ([email protected])
|
* Stefano Ceccherini ([email protected])
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! BMenuWindow is a custom BWindow for BMenus.
|
//! BMenuWindow is a custom BWindow for BMenus.
|
||||||
|
|
||||||
// TODO: Add scrollers
|
|
||||||
|
|
||||||
#include <Menu.h>
|
#include <Menu.h>
|
||||||
|
|
||||||
#include <MenuPrivate.h>
|
#include <MenuPrivate.h>
|
||||||
@@ -18,27 +17,133 @@
|
|||||||
#include <WindowPrivate.h>
|
#include <WindowPrivate.h>
|
||||||
|
|
||||||
|
|
||||||
// This draws the frame around the BMenu
|
const int kScrollerHeight = 10;
|
||||||
class BMenuFrame : public BView {
|
const int kScrollStep = 16;
|
||||||
public:
|
|
||||||
BMenuFrame(BMenu *menu);
|
|
||||||
|
|
||||||
virtual void AttachedToWindow();
|
BMenuScroller::BMenuScroller(BRect frame, BMenu *menu)
|
||||||
virtual void DetachedFromWindow();
|
: BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS
|
||||||
virtual void Draw(BRect updateRect);
|
| 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:
|
fLimit = menu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight);
|
||||||
BMenu *fMenu;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
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)
|
BMenuWindow::BMenuWindow(const char *name)
|
||||||
// The window will be resized by BMenu, so just pass a dummy rect
|
// 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,
|
: BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
|
||||||
B_NOT_ZOOMABLE | B_AVOID_FOCUS),
|
B_NOT_ZOOMABLE | B_AVOID_FOCUS),
|
||||||
fUpperScroller(NULL),
|
fScroller(NULL),
|
||||||
fLowerScroller(NULL)
|
fMenuFrame(NULL)
|
||||||
{
|
{
|
||||||
|
SetPulseRate(200000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -50,11 +155,11 @@ BMenuWindow::~BMenuWindow()
|
|||||||
void
|
void
|
||||||
BMenuWindow::AttachMenu(BMenu *menu)
|
BMenuWindow::AttachMenu(BMenu *menu)
|
||||||
{
|
{
|
||||||
if (CountChildren() > 0)
|
if (fMenuFrame)
|
||||||
debugger("BMenuWindow (%s): a menu is already attached!");
|
debugger("BMenuWindow: a menu is already attached!");
|
||||||
if (menu != NULL) {
|
if (menu != NULL) {
|
||||||
BMenuFrame *menuFrame = new BMenuFrame(menu);
|
fMenuFrame = new BMenuFrame(menu);
|
||||||
AddChild(menuFrame);
|
AddChild(fMenuFrame);
|
||||||
menu->MakeFocus(true);
|
menu->MakeFocus(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +168,53 @@ BMenuWindow::AttachMenu(BMenu *menu)
|
|||||||
void
|
void
|
||||||
BMenuWindow::DetachMenu()
|
BMenuWindow::DetachMenu()
|
||||||
{
|
{
|
||||||
if (CountChildren() > 0)
|
if (fMenuFrame) {
|
||||||
RemoveChild(ChildAt(0));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user