WIP: Created a BScrollMenu class that works like BMenuWindow but works on a view instead of a window. Use this to implement a scrollable BarMenuBar in Deskbar. The basics work but there are issues still with sizing and other issues.

Modify the ScrollMenu class to use the layout kit by adding a constructor that doesn't take a view.

Get the BScrollMenu class to follow the size of the BMenu it is a parent of. Adjust the scrollers to appear in the right places. This is a WIP but it works in Deskbar, next step is to integrate this directly into BMenu with the scrollers as children of the menu instead of as children of the BScroller class.

Rebase changes on top of master

Deskbar scrolling works for the most part, just need to fix the
bottom arrow and clean up a bit.
This commit is contained in:
John Scipione 2011-06-27 22:24:55 -04:00
parent 5ad155d720
commit cb55ef9fb5
9 changed files with 580 additions and 38 deletions

View File

@ -0,0 +1,55 @@
/*
* Copyright 2011, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Marc Flerackers (mflerackers@androme.be)
* Stefano Ceccherini (stefano.ceccherini@gmail.com)
* John Scipione (jscipione@gmail.com)
*/
#ifndef SCROLL_MENU_H
#define SCROLL_MENU_H
#include <View.h>
class BLayout;
class BMenu;
class BMenuScroller;
class BScrollMenu : public BView {
public:
BScrollMenu(BMenu* menu);
virtual ~BScrollMenu();
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect);
virtual void FrameResized(float newWidth, float newHeight);
void AttachScrollers();
void DetachScrollers();
bool HasScrollers() const;
void SetSmallStep(float step);
void GetSteps(float* _smallStep, float* _largeStep) const;
bool CheckForScrolling(const BPoint& cursor);
bool TryScrollBy(const float& step);
protected:
bool _Scroll(const BPoint& cursor);
void _ScrollBy(const float& step);
private:
BMenu* fMenu;
BMenuScroller* fUpperScroller;
BMenuScroller* fLowerScroller;
float fScrollStep;
float fValue;
float fLimit;
};
#endif // SCROLL_MENU_H

View File

@ -48,6 +48,7 @@ All rights reserved.
#include <NodeInfo.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollMenu.h>
#include <String.h>
#include "icons.h"
@ -130,6 +131,7 @@ BarViewMessageFilter::Filter(BMessage* message, BHandler** target)
TBarView::TBarView(BRect frame, bool vertical, bool left, bool top,
uint32 state, float)
: BView(frame, "BarView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
fBarScrollMenu(NULL),
fBarMenuBar(NULL),
fExpando(NULL),
fTrayLocation(1),
@ -347,8 +349,7 @@ TBarView::MouseDown(BPoint where)
void
TBarView::PlaceDeskbarMenu()
{
// top or bottom, full
if (!fVertical && fBarMenuBar) {
if (!fVertical && fBarMenuBar != NULL) {
fBarMenuBar->RemoveSelf();
delete fBarMenuBar;
fBarMenuBar = NULL;
@ -367,12 +368,13 @@ TBarView::PlaceDeskbarMenu()
// if there isn't a bemenu at this point,
// DB should be in top/bottom mode, else error
if (!fBarMenuBar)
if (fBarMenuBar == NULL)
return;
float width = sMinimumWindowWidth;
BPoint loc(B_ORIGIN);
BRect menuFrame(fBarMenuBar->Frame());
if (fState == kFullState) {
fBarMenuBar->RemoveTeamMenu();
// TODO: Magic constants need explanation
@ -441,22 +443,22 @@ TBarView::PlaceTray(bool vertSwap, bool leftSwap)
void
TBarView::PlaceApplicationBar()
{
if (fExpando != NULL) {
SaveExpandedItems();
if (fBarScrollMenu != NULL) {
fBarScrollMenu->RemoveSelf();
delete fBarScrollMenu;
// Also deletes fExpando
fBarScrollMenu = NULL;
fExpando = NULL;
} else if (fExpando != NULL) {
fExpando->RemoveSelf();
delete fExpando;
fExpando = NULL;
}
BRect screenFrame = (BScreen(Window())).Frame();
if (fState == kMiniState) {
SizeWindow(screenFrame);
PositionWindow(screenFrame);
Window()->UpdateIfNeeded();
Invalidate();
if (fState == kMiniState)
return;
}
BRect screenFrame = (BScreen(Window())).Frame();
BRect expandoFrame(0, 0, 0, 0);
if (fVertical) {
// top left/right
@ -485,6 +487,12 @@ TBarView::PlaceApplicationBar()
fExpando = new TExpandoMenuBar(this, expandoFrame, "ExpandoMenuBar",
fVertical, !hideLabels && fState != kFullState);
if (fVertical) {
fBarScrollMenu = new BScrollMenu(fExpando);
AddChild(fBarScrollMenu);
//printf("fExpando bottom: %f, fBarScrollMenu bottom: %f\n", fExpando->Frame().bottom, fBarScrollMenu->Frame().bottom);
} else
AddChild(fExpando);
if (fVertical)
@ -522,7 +530,7 @@ TBarView::GetPreferredWindowSize(BRect screenFrame, float* width, float* height)
} else if (fState == kExpandoState) {
if (fVertical) {
// top left or right
windowHeight = fExpando->Frame().bottom;
windowHeight = fBarScrollMenu->Frame().bottom;
} else {
// top or bottom, full
fExpando->CheckItemSizes(0);

View File

@ -65,7 +65,7 @@ const float kStatusHeight = 22.0f;
const float kHiddenDimension = 1.0f;
const float kMaxPreventHidingDist = 80.0f;
class BScrollMenu;
class BShelf;
class TBarMenuBar;
class TExpandoMenuBar;
@ -168,6 +168,7 @@ class TBarView : public BView {
void ExpandItems();
void _ChangeState(BMessage* message);
BScrollMenu* fBarScrollMenu;
TBarMenuBar* fBarMenuBar;
TExpandoMenuBar* fExpando;

View File

@ -45,6 +45,7 @@ All rights reserved.
#include <NodeInfo.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollMenu.h>
#include "icons.h"
@ -797,10 +798,22 @@ TExpandoMenuBar::DrawBackground(BRect)
void
TExpandoMenuBar::CheckForSizeOverrun()
{
BRect screenFrame = (BScreen(Window())).Frame();
if (!fVertical) {
fIsScrolling = false;
return;
}
fIsScrolling = fVertical ? Window()->Frame().bottom > screenFrame.bottom
: false;
BScrollMenu* scrollMenu = dynamic_cast<BScrollMenu*>(Parent());
if (scrollMenu == NULL)
return;
BRect screenFrame = (BScreen(Window())).Frame();
fIsScrolling = Window()->Frame().bottom > screenFrame.bottom;
if (fIsScrolling)
scrollMenu->AttachScrollers();
else
scrollMenu->DetachScrollers();
}

View File

@ -479,8 +479,7 @@ TTeamMenuItem::DrawContentLabel()
if (Submenu() && fVertical)
cachedWidth += 18;
const char* label = Label();
char* truncLabel = NULL;
BString label(Label());
float max = 0;
if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando)
@ -492,30 +491,23 @@ TTeamMenuItem::DrawContentLabel()
BPoint penloc = menu->PenLocation();
BRect frame = Frame();
float offset = penloc.x - frame.left;
if (cachedWidth + offset > max) {
truncLabel = (char*)malloc(strlen(label) + 4);
if (!truncLabel)
return;
TruncateLabel(max-offset, truncLabel);
label = truncLabel;
}
if (cachedWidth + offset > max)
menu->TruncateString(&label, B_TRUNCATE_MIDDLE, max - offset);
}
if (!label)
label = Label();
label = BString(Label());
TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView();
bool canHandle = !barview->Dragging()
|| barview->AppCanHandleTypes(Signature());
TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
bool canHandle = !barView->Dragging()
|| barView->AppCanHandleTypes(Signature());
if (_IsSelected() && IsEnabled() && canHandle)
menu->SetLowColor(tint_color(menu->LowColor(),
B_HIGHLIGHT_BACKGROUND_TINT));
else
menu->SetLowColor(menu->LowColor());
menu->DrawString(label);
free(truncLabel);
menu->DrawString(label.String());
}

View File

@ -101,6 +101,7 @@ MergeObject <libbe>interface_kit.o :
RegionSupport.cpp
Screen.cpp
ScrollBar.cpp
ScrollMenu.cpp
ScrollView.cpp
SeparatorItem.cpp
SeparatorView.cpp

View File

@ -30,6 +30,7 @@
#include <PropertyInfo.h>
#include <Screen.h>
#include <ScrollBar.h>
#include <ScrollMenu.h>
#include <SystemCatalog.h>
#include <Window.h>
@ -2811,13 +2812,18 @@ BMenu::_ChooseTrigger(const char* title, int32& index, uint32& trigger,
void
BMenu::_UpdateWindowViewSize(const bool &move)
{
if (dynamic_cast<BMenuBar*>(this) != NULL) {
BScrollMenu* scrollMenu = dynamic_cast<BScrollMenu*>(Parent());
if (scrollMenu != NULL)
scrollMenu->ResizeTo(Bounds().Width(), Bounds().Height());
return;
}
BMenuWindow* window = static_cast<BMenuWindow*>(Window());
if (window == NULL)
return;
if (dynamic_cast<BMenuBar*>(this) != NULL)
return;
if (!fResizeToFit)
return;

View File

@ -18,6 +18,7 @@
#include <ControlLook.h>
#include <LayoutUtils.h>
#include <MenuItem.h>
#include <ScrollMenu.h>
#include <Window.h>
#include <AppMisc.h>
@ -328,7 +329,36 @@ BMenuBar::Draw(BRect updateRect)
void
BMenuBar::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case B_MOUSE_WHEEL_CHANGED:
{
float deltaY = 0;
msg->FindFloat("be:wheel_delta_y", &deltaY);
if (deltaY == 0)
return;
BScrollMenu* scrollMenu = dynamic_cast<BScrollMenu*>(Parent());
if (scrollMenu == NULL)
return;
float largeStep;
float smallStep;
scrollMenu->GetSteps(&smallStep, &largeStep);
// pressing the option/command/control key scrolls faster
if (modifiers() & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY))
deltaY *= largeStep;
else
deltaY *= smallStep;
scrollMenu->TryScrollBy(deltaY);
break;
}
default:
BMenu::MessageReceived(msg);
break;
}
}

View File

@ -0,0 +1,436 @@
/*
* Copyright 2011, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Marc Flerackers (mflerackers@androme.be)
* Stefano Ceccherini (stefano.ceccherini@gmail.com)
* John Scipione (jscipione@gmail.com)
*/
#include <ScrollMenu.h>
#include <ControlLook.h>
#include <Debug.h>
#include <InterfaceDefs.h>
#include <Layout.h>
#include <GroupLayout.h>
#include <Menu.h>
#include <Screen.h>
#include <MenuPrivate.h>
const char* kEmptyMenuLabel = "<empty>";
const int kDefaultScrollStep = 19;
const int kScrollerHeight = 12;
class BMenuScroller : public BView {
public:
BMenuScroller(BRect frame);
virtual ~BMenuScroller();
bool IsEnabled() const { return fEnabled; };
void SetEnabled(bool enabled);
private:
bool fEnabled;
};
class BMenuUpScroller : public BMenuScroller {
public:
BMenuUpScroller(BRect frame);
virtual ~BMenuUpScroller();
virtual void Draw(BRect updateRect);
};
class BMenuDownScroller : public BMenuScroller {
public:
BMenuDownScroller(BRect frame);
virtual ~BMenuDownScroller();
virtual void Draw(BRect updateRect);
};
// #pragma mark -
BMenuScroller::BMenuScroller(BRect frame)
:
BView(frame, "menu scroll arrow", 0, B_WILL_DRAW | B_FRAME_EVENTS),
fEnabled(false)
{
SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
}
BMenuScroller::~BMenuScroller()
{
}
void
BMenuScroller::SetEnabled(bool enabled)
{
fEnabled = enabled;
Invalidate();
}
// #pragma mark -
BMenuUpScroller::BMenuUpScroller(BRect frame)
:
BMenuScroller(frame)
{
}
BMenuUpScroller::~BMenuUpScroller()
{
}
void
BMenuUpScroller::Draw(BRect updateRect)
{
SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
// Draw the upper arrow.
if (IsEnabled())
SetHighColor(0, 0, 0);
else {
SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
B_DARKEN_2_TINT));
}
FillRect(Bounds(), B_SOLID_LOW);
float middle = Bounds().right / 2;
FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3),
BPoint(middle + 5, (kScrollerHeight / 2) + 2),
BPoint(middle - 5, (kScrollerHeight / 2) + 2));
}
// #pragma mark -
BMenuDownScroller::BMenuDownScroller(BRect frame)
:
BMenuScroller(frame)
{
}
BMenuDownScroller::~BMenuDownScroller()
{
}
void
BMenuDownScroller::Draw(BRect updateRect)
{
SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
// Draw the lower arrow.
if (IsEnabled())
SetHighColor(0, 0, 0);
else {
SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
B_DARKEN_2_TINT));
}
BRect frame = Bounds();
FillRect(frame, B_SOLID_LOW);
float middle = Bounds().right / 2;
FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3),
BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2),
BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2));
}
// #pragma mark -
BScrollMenu::BScrollMenu(BMenu *menu)
:
BView("menu scroll view", B_WILL_DRAW | B_FRAME_EVENTS),
fMenu(menu),
fUpperScroller(NULL),
fLowerScroller(NULL),
fScrollStep(kDefaultScrollStep)
{
}
BScrollMenu::~BScrollMenu()
{
if (fMenu != NULL) {
fMenu->RemoveSelf();
delete fMenu;
fMenu = NULL;
}
if (fUpperScroller != NULL) {
fUpperScroller->RemoveSelf();
delete fUpperScroller;
fUpperScroller = NULL;
}
if (fLowerScroller != NULL) {
fLowerScroller->RemoveSelf();
delete fLowerScroller;
fLowerScroller = NULL;
}
}
void
BScrollMenu::AttachedToWindow()
{
BView::AttachedToWindow();
if (fMenu == NULL)
return;
AddChild(fMenu);
// Move the scroll menu into the right position
MoveTo(fMenu->Frame().LeftTop());
BFont font;
fMenu->GetFont(&font);
SetFont(&font);
}
void
BScrollMenu::DetachedFromWindow()
{
BView::DetachedFromWindow();
if (fMenu != NULL)
fMenu->RemoveSelf();
if (fUpperScroller != NULL)
fUpperScroller->RemoveSelf();
if (fLowerScroller != NULL)
fLowerScroller->RemoveSelf();
}
void
BScrollMenu::Draw(BRect updateRect)
{
if (be_control_look != NULL)
return;
SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT));
BRect bounds(Bounds());
StrokeLine(BPoint(bounds.right, bounds.top),
BPoint(bounds.right, bounds.bottom - 1));
StrokeLine(BPoint(bounds.left + 1, bounds.bottom),
BPoint(bounds.right, bounds.bottom));
}
void
BScrollMenu::FrameResized(float newWidth, float newHeight)
{
BView::FrameResized(newWidth, newHeight);
if (fMenu != NULL) {
if (HasScrollers())
fMenu->MoveTo(0, kScrollerHeight);
else
fMenu->MoveTo(0, 0);
}
}
void
BScrollMenu::AttachScrollers()
{
if (fMenu == NULL)
return;
BRect frame = Bounds();
BRect screenFrame = (BScreen(Window())).Frame();
if (HasScrollers()) {
fLimit = Frame().bottom + 2 * kScrollerHeight - screenFrame.bottom;
return;
}
fMenu->MakeFocus(true);
if (fUpperScroller == NULL) {
fUpperScroller = new BMenuUpScroller(
BRect(0, 0, frame.right, kScrollerHeight - 1));
AddChild(fUpperScroller, fMenu);
}
if (fLowerScroller == NULL) {
fLowerScroller = new BMenuDownScroller(
BRect(0, frame.bottom - kScrollerHeight + 1, frame.right,
frame.bottom));
AddChild(fLowerScroller);
}
fUpperScroller->SetEnabled(false);
fLowerScroller->SetEnabled(true);
fLimit = Frame().bottom + 2 * kScrollerHeight - screenFrame.bottom;
fValue = 0;
}
void
BScrollMenu::DetachScrollers()
{
if (!HasScrollers())
return;
if (fLowerScroller) {
fLowerScroller->RemoveSelf();
delete fLowerScroller;
fLowerScroller = NULL;
}
if (fUpperScroller) {
fUpperScroller->RemoveSelf();
delete fUpperScroller;
fUpperScroller = NULL;
}
if (fMenu) {
// We don't remember the position where the last scrolling
// ended, so scroll back to the beginning.
fMenu->ScrollTo(0, 0);
// Since the scrollers were removed move back up.
//fMenu->ResizeBy(0, 2 * kScrollerHeight);
//fMenu->MoveBy(0, -kScrollerHeight);
fValue = 0;
}
}
bool
BScrollMenu::HasScrollers() const
{
return fMenu != NULL && fUpperScroller != NULL && fLowerScroller != NULL;
}
void
BScrollMenu::SetSmallStep(float step)
{
fScrollStep = step;
}
void
BScrollMenu::GetSteps(float* _smallStep, float* _largeStep) const
{
if (_smallStep != NULL)
*_smallStep = fScrollStep;
if (_largeStep != NULL) {
if (fMenu != NULL)
*_largeStep = fMenu->Frame().Height() - fScrollStep;
else
*_largeStep = fScrollStep * 2;
}
}
bool
BScrollMenu::CheckForScrolling(const BPoint &cursor)
{
if (!HasScrollers())
return false;
return _Scroll(cursor);
}
bool
BScrollMenu::TryScrollBy(const float& step)
{
if (!HasScrollers())
return false;
_ScrollBy(step);
return true;
}
bool
BScrollMenu::_Scroll(const BPoint& where)
{
ASSERT((fLowerScroller != NULL));
ASSERT((fUpperScroller != NULL));
const BPoint cursor = ConvertFromScreen(where);
const BRect &lowerFrame = fLowerScroller->Frame();
const BRect &upperFrame = fUpperScroller->Frame();
int32 delta = 0;
if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor))
delta = 1;
else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor))
delta = -1;
if (delta == 0)
return false;
float smallStep;
GetSteps(&smallStep, NULL);
_ScrollBy(smallStep * delta);
snooze(5000);
return true;
}
void
BScrollMenu::_ScrollBy(const float& step)
{
if (step > 0) {
if (fValue == 0)
fUpperScroller->SetEnabled(true);
if (fValue + step >= fLimit) {
// If we reached the limit, only scroll to the end
fMenu->ScrollBy(0, fLimit - fValue);
fValue = fLimit;
fLowerScroller->SetEnabled(false);
} else {
fMenu->ScrollBy(0, step);
fValue += step;
}
} else if (step < 0) {
if (fValue == fLimit)
fLowerScroller->SetEnabled(true);
if (fValue + step <= 0) {
fMenu->ScrollBy(0, -fValue);
fValue = 0;
fUpperScroller->SetEnabled(false);
} else {
fMenu->ScrollBy(0, step);
fValue += step;
}
}
}