1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added TabBarButton::setExtraComponent() method to allow custom components to be inserted into tabs. This involved a big refactoring and clean-up of all the tab drawing methods in the LookAndFeel class. Also (slightly) cleaned up some of the crappy old code in the WidgetsDemo while I was adding a demo for this.

This commit is contained in:
jules 2012-07-11 15:12:29 +01:00
parent 1e9e6cbf79
commit 591ce2a396
5 changed files with 412 additions and 386 deletions

View file

@ -33,60 +33,51 @@ class BouncingBallComponent : public Component,
public:
BouncingBallComponent()
{
x = Random::getSystemRandom().nextFloat() * 100.0f;
y = Random::getSystemRandom().nextFloat() * 100.0f;
Random random;
dx = Random::getSystemRandom().nextFloat() * 8.0f - 4.0f;
dy = Random::getSystemRandom().nextFloat() * 8.0f - 4.0f;
const int size = 10 + random.nextInt (30);
colour = Colour (Random::getSystemRandom().nextInt())
ballBounds.setBounds (random.nextFloat() * 100.0f,
random.nextFloat() * 100.0f,
size, size);
direction.x = random.nextFloat() * 8.0f - 4.0f;
direction.y = random.nextFloat() * 8.0f - 4.0f;
colour = Colour (random.nextInt())
.withAlpha (0.5f)
.withBrightness (0.7f);
int size = 10 + Random::getSystemRandom().nextInt (30);
setSize (size, size);
startTimer (60);
}
~BouncingBallComponent()
{
}
void paint (Graphics& g)
{
g.setColour (colour);
g.fillEllipse (x - getX(), y - getY(), getWidth() - 2.0f, getHeight() - 2.0f);
g.fillEllipse (ballBounds - getPosition().toFloat());
}
void timerCallback()
{
x += dx;
y += dy;
ballBounds += direction;
if (x < 0)
dx = fabsf (dx);
if (ballBounds.getX() < 0) direction.x = fabsf (direction.x);
if (ballBounds.getY() < 0) direction.y = fabsf (direction.y);
if (ballBounds.getRight() > getParentWidth()) direction.x = -fabsf (direction.x);
if (ballBounds.getBottom() > getParentHeight()) direction.y = -fabsf (direction.y);
if (x > getParentWidth())
dx = -fabsf (dx);
if (y < 0)
dy = fabsf (dy);
if (y > getParentHeight())
dy = -fabsf (dy);
setTopLeftPosition ((int) x, (int) y);
setBounds (ballBounds.getSmallestIntegerContainer());
}
bool hitTest (int /*x*/, int /*y*/)
bool hitTest (int /* x */, int /* y */)
{
return false;
}
private:
Colour colour;
float x, y, dx, dy;
Rectangle<float> ballBounds;
Point<float> direction;
};
//==============================================================================
@ -103,10 +94,6 @@ public:
addAndMakeVisible (&(balls[i]));
}
~DragOntoDesktopDemoComp()
{
}
void mouseDown (const MouseEvent& e)
{
dragger.startDraggingComponent (this, e);
@ -114,7 +101,7 @@ public:
void mouseDrag (const MouseEvent& e)
{
if (parent == 0)
if (parent == nullptr)
{
delete this; // If our parent has been deleted, we'll just get rid of this component
}
@ -146,17 +133,16 @@ public:
else
g.fillAll (Colours::blue.withAlpha (0.2f));
String desc ("drag this box onto the desktop to show how the same component can move from being lightweight to being a separate window");
g.setFont (15.0f);
g.setColour (Colours::black);
g.drawFittedText (desc, 4, 0, getWidth() - 8, getHeight(), Justification::horizontallyJustified, 5);
g.drawFittedText ("drag this box onto the desktop to show how the same component can move from being lightweight to being a separate window",
4, 0, getWidth() - 8, getHeight(), Justification::horizontallyJustified, 5);
g.drawRect (0, 0, getWidth(), getHeight());
g.drawRect (getLocalBounds());
}
private:
Component::SafePointer<Component> parent; // A safe-pointer will become zero if the component that it refers to is deleted..
Component::SafePointer<Component> parent; // A safe-pointer will become null if the component that it refers to is deleted..
ComponentDragger dragger;
BouncingBallComponent balls[3];
@ -168,18 +154,12 @@ class CustomMenuComponent : public PopupMenu::CustomComponent,
{
public:
CustomMenuComponent()
: blobX (0),
blobY (0)
{
// set off a timer to move a blob around on this component every
// 300 milliseconds - see the timerCallback() method.
startTimer (300);
}
~CustomMenuComponent()
{
}
void getIdealSize (int& idealWidth,
int& idealHeight)
{
@ -193,7 +173,7 @@ public:
g.fillAll (Colours::yellow.withAlpha (0.3f));
g.setColour (Colours::pink);
g.fillEllipse ((float) blobX, (float) blobY, 30.0f, 40.0f);
g.fillEllipse (blobPosition);
g.setFont (Font (14.0f, Font::italic));
g.setColour (Colours::black);
@ -205,13 +185,15 @@ public:
void timerCallback()
{
blobX = Random::getSystemRandom().nextInt (getWidth());
blobY = Random::getSystemRandom().nextInt (getHeight());
Random random;
blobPosition.setBounds (random.nextInt (getWidth()),
random.nextInt (getHeight()),
40, 30);
repaint();
}
private:
int blobX, blobY;
Rectangle<float> blobPosition;
};
//==============================================================================
@ -250,23 +232,16 @@ public:
changeWidthToFitText();
}
~ColourChangeButton()
{
}
void clicked()
{
#if JUCE_MODAL_LOOPS_PERMITTED
ColourSelector colourSelector;
colourSelector.setName ("background");
colourSelector.setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector.addChangeListener (this);
colourSelector.setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector.setSize (300, 400);
ColourSelector* colourSelector = new ColourSelector();
colourSelector->setName ("background");
colourSelector->setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector->addChangeListener (this);
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector->setSize (300, 400);
CallOutBox callOut (colourSelector, *this, 0);
callOut.runModalLoop();
#endif
CallOutBox::launchAsynchronously (*this, colourSelector, nullptr);
}
void changeListenerCallback (ChangeBroadcaster* source)
@ -417,8 +392,7 @@ static Component* createRadioButtonPage()
group->setBounds (20, 20, 220, 140);
page->addAndMakeVisible (group);
int i;
for (i = 0; i < 4; ++i)
for (int i = 0; i < 4; ++i)
{
ToggleButton* tb = new ToggleButton ("radio button #" + String (i + 1));
page->addAndMakeVisible (tb);
@ -430,7 +404,7 @@ static Component* createRadioButtonPage()
tb->setToggleState (true, false);
}
for (i = 0; i < 4; ++i)
for (int i = 0; i < 4; ++i)
{
DrawablePath normal, over;
@ -460,7 +434,7 @@ static Component* createRadioButtonPage()
db->setToggleState (true, false);
}
for (i = 0; i < 4; ++i)
for (int i = 0; i < 4; ++i)
{
TextButton* tb = new TextButton ("button " + String (i + 1));
@ -512,41 +486,37 @@ public:
// create an image-above-text button from these drawables..
DrawableButton* db = new DrawableButton ("Button 1", DrawableButton::ImageAboveTextLabel);
db->setImages (&normal, &over, &down);
addAndMakeVisible (db);
db->setBounds (10, 30, 80, 80);
db->setTooltip ("this is a DrawableButton with a label");
addAndMakeVisible (db);
//==============================================================================
// create an image-only button from these drawables..
db = new DrawableButton ("Button 2", DrawableButton::ImageFitted);
db->setImages (&normal, &over, &down);
db->setClickingTogglesState (true);
addAndMakeVisible (db);
db->setBounds (90, 30, 80, 80);
db->setTooltip ("this is an image-only DrawableButton");
db->addListener (buttonListener);
addAndMakeVisible (db);
//==============================================================================
// create an image-on-button-shape button from the same drawables..
db = new DrawableButton ("Button 3", DrawableButton::ImageOnButtonBackground);
db->setImages (&normal, 0, 0);
addAndMakeVisible (db);
db->setBounds (200, 30, 110, 25);
db->setTooltip ("this is a DrawableButton on a standard button background");
addAndMakeVisible (db);
//==============================================================================
db = new DrawableButton ("Button 4", DrawableButton::ImageOnButtonBackground);
db->setImages (&normal, &over, &down);
db->setClickingTogglesState (true);
db->setBackgroundColours (Colours::white, Colours::yellow);
addAndMakeVisible (db);
db->setBounds (200, 70, 50, 50);
db->setTooltip ("this is a DrawableButton on a standard button background");
db->addListener (buttonListener);
addAndMakeVisible (db);
//==============================================================================
HyperlinkButton* hyperlink
@ -577,10 +547,10 @@ public:
//==============================================================================
animateButton = new TextButton ("click to animate...");
addAndMakeVisible (animateButton);
animateButton->changeWidthToFitText (24);
animateButton->setTopLeftPosition (350, 70);
animateButton->addListener (this);
addAndMakeVisible (animateButton);
}
~ButtonsPage()
@ -642,8 +612,7 @@ static Component* createMiscPage()
comboBox->setEditableText (true);
comboBox->setJustificationType (Justification::centred);
int i;
for (i = 1; i < 100; ++i)
for (int i = 1; i < 100; ++i)
comboBox->addItem ("combo box item " + String (i), i);
comboBox->setSelectedId (1);
@ -705,10 +674,12 @@ public:
void resized()
{
int toolbarThickness = (int) depthSlider.getValue();
if (toolbar.isVertical())
toolbar.setBounds (0, 0, (int) depthSlider.getValue(), getHeight());
toolbar.setBounds (getLocalBounds().removeFromLeft (toolbarThickness));
else
toolbar.setBounds (0, 0, getWidth(), (int) depthSlider.getValue());
toolbar.setBounds (getLocalBounds().removeFromTop (toolbarThickness));
}
void sliderValueChanged (Slider*)
@ -740,7 +711,6 @@ private:
{
public:
DemoToolbarItemFactory() {}
~DemoToolbarItemFactory() {}
//==============================================================================
// Each type of item a toolbar can contain must be given a unique ID. These
@ -870,10 +840,6 @@ private:
comboBox.setEditableText (true);
}
~CustomToolbarComboBox()
{
}
bool getToolbarItemSizes (int /*toolbarDepth*/, bool isToolbarVertical,
int& preferredSize, int& minSize, int& maxSize)
{
@ -919,9 +885,19 @@ public:
addTab ("buttons", getRandomBrightColour(), new ButtonsPage (this), true);
addTab ("radio buttons", getRandomBrightColour(), createRadioButtonPage(), true);
addTab ("misc widgets", getRandomBrightColour(), createMiscPage(), true);
getTabbedButtonBar().getTabButton (2)->setExtraComponent (new CustomTabButton(), TabBarButton::afterText);
}
void buttonClicked (Button* button)
{
showBubbleMessage (button,
"This is a demo of the BubbleMessageComponent, which lets you pop up a message pointing "
"at a component or somewhere on the screen.\n\n"
"The message bubbles will disappear after a timeout period, or when the mouse is clicked.");
}
void showBubbleMessage (Component* targetComponent, const String& textToShow)
{
BubbleMessageComponent* bmc = new BubbleMessageComponent();
@ -935,18 +911,44 @@ public:
addChildComponent (bmc);
}
AttributedString text ("This is a demo of the BubbleMessageComponent, which lets you pop up a message pointing "
"at a component or somewhere on the screen.\n\n"
"The message bubbles will disappear after a timeout period, or when the mouse is clicked.");
AttributedString text (textToShow);
text.setJustification (Justification::centred);
bmc->showAt (button, text, 2000, true, true);
bmc->showAt (targetComponent, text, 2000, true, true);
}
static const Colour getRandomBrightColour()
{
return Colour (Random::getSystemRandom().nextFloat(), 0.1f, 0.97f, 1.0f);
}
// This is a small star button that is put inside one of the tabs. You can
// use this technique to create things like "close tab" buttons, etc.
class CustomTabButton : public Component
{
public:
CustomTabButton()
{
setSize (20, 20);
}
void paint (Graphics& g)
{
Path p;
p.addStar (Point<float>(), 7, 1.0f, 2.0f);
g.setColour (Colours::green);
g.fillPath (p, RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (p.getBounds(), getLocalBounds().reduced (2, 2).toFloat()));
}
void mouseDown (const MouseEvent&)
{
DemoTabbedComponent* dtc = findParentComponentOfClass<DemoTabbedComponent>();
dtc->showBubbleMessage (this, "This is a custom tab component");
}
};
};
@ -997,9 +999,7 @@ class ColourSelectorDialogWindow : public DialogWindow
{
public:
ColourSelectorDialogWindow()
: DialogWindow ("Colour selector demo",
Colours::lightgrey,
true)
: DialogWindow ("Colour selector demo", Colours::lightgrey, true)
{
setContentOwned (new ColourSelector(), false);
centreWithSize (400, 400);
@ -1044,29 +1044,27 @@ public:
void buttonPressed (const ButtonType buttonId, const bool isDown)
{
String desc;
setMessage (getDescriptionOfButtonType (buttonId) + (isDown ? " -- [down]"
: " -- [up]"));
}
switch (buttonId)
static String getDescriptionOfButtonType (const ButtonType type)
{
switch (type)
{
case menuButton: desc = "menu button (short)"; break;
case playButton: desc = "play button"; break;
case plusButton: desc = "plus button"; break;
case minusButton: desc = "minus button"; break;
case rightButton: desc = "right button (short)"; break;
case leftButton: desc = "left button (short)"; break;
case rightButton_Long: desc = "right button (long)"; break;
case leftButton_Long: desc = "left button (long)"; break;
case menuButton_Long: desc = "menu button (long)"; break;
case playButtonSleepMode: desc = "play (sleep mode)"; break;
case switched: desc = "remote switched"; break;
case menuButton: return "menu button (short)";
case playButton: return "play button";
case plusButton: return "plus button";
case minusButton: return "minus button";
case rightButton: return "right button (short)";
case leftButton: return "left button (short)";
case rightButton_Long: return "right button (long)";
case leftButton_Long: return "left button (long)";
case menuButton_Long: return "menu button (long)";
case playButtonSleepMode: return "play (sleep mode)";
case switched: return "remote switched";
default: return "unknown";
}
if (isDown)
desc << " -- [down]";
else
desc << " -- [up]";
setMessage (desc);
}
};
@ -1093,7 +1091,7 @@ public:
menuButton.setBounds (10, 10, 200, 24);
menuButton.addListener (this);
menuButton.setTriggeredOnMouseDown (true); // because this button pops up a menu, this lets us
// hold down the button and drag straight onto the menu
// hold down the button and drag straight onto the menu
//==============================================================================
addAndMakeVisible (&enableButton);
@ -1135,7 +1133,6 @@ public:
m.addColouredItem (4, "Coloured item", Colours::green);
m.addSeparator();
m.addCustomItem (5, new CustomMenuComponent());
m.addSeparator();
PopupMenu tabsMenu;
@ -1143,8 +1140,8 @@ public:
tabsMenu.addItem (1002, "Show tabs at the bottom", true, tabs.getOrientation() == TabbedButtonBar::TabsAtBottom);
tabsMenu.addItem (1003, "Show tabs at the left", true, tabs.getOrientation() == TabbedButtonBar::TabsAtLeft);
tabsMenu.addItem (1004, "Show tabs at the right", true, tabs.getOrientation() == TabbedButtonBar::TabsAtRight);
m.addSubMenu ("Tab position", tabsMenu);
m.addSubMenu ("Tab position", tabsMenu);
m.addSeparator();
PopupMenu dialogMenu;
@ -1154,28 +1151,22 @@ public:
dialogMenu.addItem (103, "Show an alert-window with a 'question' icon...");
dialogMenu.addSeparator();
dialogMenu.addItem (110, "Show an ok/cancel alert-window...");
dialogMenu.addSeparator();
dialogMenu.addItem (111, "Show an alert-window with some extra components...");
dialogMenu.addSeparator();
dialogMenu.addItem (112, "Show a ThreadWithProgressWindow demo...");
m.addSubMenu ("AlertWindow demonstrations", dialogMenu);
m.addSeparator();
m.addItem (120, "Show a colour selector demo...");
m.addSeparator();
#if JUCE_MAC
#if JUCE_MAC
m.addItem (140, "Run the Apple Remote Control test...");
m.addSeparator();
#endif
#endif
PopupMenu nativeFileChoosers;
nativeFileChoosers.addItem (121, "'Load' file browser...");
@ -1221,12 +1212,12 @@ public:
{
AlertWindow::AlertIconType icon = AlertWindow::NoIcon;
if (result == 101)
icon = AlertWindow::WarningIcon;
else if (result == 102)
icon = AlertWindow::InfoIcon;
else if (result == 103)
icon = AlertWindow::QuestionIcon;
switch (result)
{
case 101: icon = AlertWindow::WarningIcon; break;
case 102: icon = AlertWindow::InfoIcon; break;
case 103: icon = AlertWindow::QuestionIcon; break;
}
AlertWindow::showMessageBoxAsync (icon,
"This is an AlertWindow",
@ -1252,14 +1243,10 @@ public:
w.addTextEditor ("text", "enter some text here", "text field:");
StringArray options;
options.add ("option 1");
options.add ("option 2");
options.add ("option 3");
options.add ("option 4");
w.addComboBox ("option", options, "some options");
const char* options[] = { "option 1", "option 2", "option 3", "option 4", nullptr };
w.addComboBox ("option", StringArray (options), "some options");
w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0));
w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0));
w.addButton ("cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0));
if (w.runModalLoop() != 0) // is they picked 'ok'

View file

@ -26,35 +26,23 @@
TabBarButton::TabBarButton (const String& name, TabbedButtonBar& owner_)
: Button (name),
owner (owner_),
overlapPixels (0)
overlapPixels (0),
extraCompPlacement (afterText)
{
shadow.setShadowProperties (2.2f, 0.7f, 0, 0);
setComponentEffect (&shadow);
setWantsKeyboardFocus (false);
}
TabBarButton::~TabBarButton()
{
}
TabBarButton::~TabBarButton() {}
int TabBarButton::getIndex() const
{
return owner.indexOfTabButton (this);
}
int TabBarButton::getIndex() const { return owner.indexOfTabButton (this); }
Colour TabBarButton::getTabBackgroundColour() const { return owner.getTabBackgroundColour (getIndex()); }
bool TabBarButton::isFrontTab() const { return getToggleState(); }
void TabBarButton::paintButton (Graphics& g,
bool isMouseOverButton,
bool isButtonDown)
void TabBarButton::paintButton (Graphics& g, const bool isMouseOverButton, const bool isButtonDown)
{
const Rectangle<int> area (getActiveArea());
g.setOrigin (area.getX(), area.getY());
getLookAndFeel().drawTabButton (g, area.getWidth(), area.getHeight(),
owner.getTabBackgroundColour (getIndex()),
getIndex(), getButtonText(), *this,
owner.getOrientation(),
isMouseOverButton, isButtonDown,
getToggleState());
getLookAndFeel().drawTabButton (*this, g, isMouseOverButton, isButtonDown);
}
void TabBarButton::clicked (const ModifierKeys& mods)
@ -69,25 +57,21 @@ bool TabBarButton::hitTest (int mx, int my)
{
const Rectangle<int> area (getActiveArea());
if (owner.getOrientation() == TabbedButtonBar::TabsAtLeft
|| owner.getOrientation() == TabbedButtonBar::TabsAtRight)
if (owner.isVertical())
{
if (isPositiveAndBelow (mx, getWidth())
&& my >= area.getY() + overlapPixels
&& my < area.getBottom() - overlapPixels)
&& my >= area.getY() + overlapPixels && my < area.getBottom() - overlapPixels)
return true;
}
else
{
if (mx >= area.getX() + overlapPixels && mx < area.getRight() - overlapPixels
&& isPositiveAndBelow (my, getHeight()))
if (isPositiveAndBelow (my, getHeight())
&& mx >= area.getX() + overlapPixels && mx < area.getRight() - overlapPixels)
return true;
}
Path p;
getLookAndFeel().createTabButtonShape (p, area.getWidth(), area.getHeight(),
getIndex(), getButtonText(), *this,
owner.getOrientation(), false, false, getToggleState());
getLookAndFeel().createTabButtonShape (*this, p, false, false);
return p.contains ((float) (mx - area.getX()),
(float) (my - area.getY()));
@ -95,24 +79,98 @@ bool TabBarButton::hitTest (int mx, int my)
int TabBarButton::getBestTabLength (const int depth)
{
return jlimit (depth * 2,
depth * 7,
getLookAndFeel().getTabButtonBestWidth (getIndex(), getButtonText(), depth, *this));
int textWidth = getLookAndFeel().getTabButtonBestWidth (*this, depth);
int extraCompSize = extraComponent != nullptr ? (owner.isVertical() ? extraComponent->getHeight()
: extraComponent->getWidth()) : 0;
return jlimit (depth * 2, depth * 8, textWidth + extraCompSize);
}
Rectangle<int> TabBarButton::getActiveArea()
void TabBarButton::calcAreas (Rectangle<int>& extraComp, Rectangle<int>& text) const
{
text = getActiveArea();
const int depth = owner.isVertical() ? text.getWidth() : text.getHeight();
const int indent = getLookAndFeel().getTabButtonOverlap (depth);
if (owner.isVertical())
text.reduce (0, indent);
else
text.reduce (indent, 0);
if (extraComponent != nullptr)
{
if (extraCompPlacement == beforeText)
{
switch (owner.getOrientation())
{
case TabbedButtonBar::TabsAtLeft: extraComp = text.removeFromBottom (extraComponent->getHeight()); break;
case TabbedButtonBar::TabsAtRight: extraComp = text.removeFromTop (extraComponent->getHeight()); break;
case TabbedButtonBar::TabsAtBottom:
case TabbedButtonBar::TabsAtTop: extraComp = text.removeFromLeft (extraComponent->getWidth()); break;
default: jassertfalse; break;
}
}
else
{
switch (owner.getOrientation())
{
case TabbedButtonBar::TabsAtLeft: extraComp = text.removeFromTop (extraComponent->getHeight()); break;
case TabbedButtonBar::TabsAtRight: extraComp = text.removeFromBottom (extraComponent->getHeight()); break;
case TabbedButtonBar::TabsAtBottom:
case TabbedButtonBar::TabsAtTop: extraComp = text.removeFromRight (extraComponent->getWidth()); break;
default: jassertfalse; break;
}
}
}
}
Rectangle<int> TabBarButton::getTextArea() const
{
Rectangle<int> extraComp, text;
calcAreas (extraComp, text);
return text;
}
Rectangle<int> TabBarButton::getActiveArea() const
{
Rectangle<int> r (getLocalBounds());
const int spaceAroundImage = getLookAndFeel().getTabButtonSpaceAroundImage();
const TabbedButtonBar::Orientation orientation = owner.getOrientation();
if (owner.getOrientation() != TabbedButtonBar::TabsAtLeft) r.removeFromRight (spaceAroundImage);
if (owner.getOrientation() != TabbedButtonBar::TabsAtRight) r.removeFromLeft (spaceAroundImage);
if (owner.getOrientation() != TabbedButtonBar::TabsAtBottom) r.removeFromTop (spaceAroundImage);
if (owner.getOrientation() != TabbedButtonBar::TabsAtTop) r.removeFromBottom (spaceAroundImage);
if (orientation != TabbedButtonBar::TabsAtLeft) r.removeFromRight (spaceAroundImage);
if (orientation != TabbedButtonBar::TabsAtRight) r.removeFromLeft (spaceAroundImage);
if (orientation != TabbedButtonBar::TabsAtBottom) r.removeFromTop (spaceAroundImage);
if (orientation != TabbedButtonBar::TabsAtTop) r.removeFromBottom (spaceAroundImage);
return r;
}
void TabBarButton::setExtraComponent (Component* comp, ExtraComponentPlacement placement)
{
jassert (extraCompPlacement == beforeText || extraCompPlacement == afterText);
extraCompPlacement = placement;
addAndMakeVisible (extraComponent = comp);
resized();
}
void TabBarButton::childBoundsChanged (Component* c)
{
if (c == extraComponent)
resized();
}
void TabBarButton::resized()
{
if (extraComponent != nullptr)
{
Rectangle<int> extraComp, text;
calcAreas (extraComp, text);
if (! extraComp.isEmpty())
extraComponent->setBounds (extraComp);
}
}
//==============================================================================
class TabbedButtonBar::BehindFrontTabComp : public Component,
@ -127,8 +185,7 @@ public:
void paint (Graphics& g)
{
getLookAndFeel().drawTabAreaBehindFrontButton (g, getWidth(), getHeight(),
owner, owner.getOrientation());
getLookAndFeel().drawTabAreaBehindFrontButton (owner, g, getWidth(), getHeight());
}
void enablementChanged()
@ -211,12 +268,12 @@ void TabbedButtonBar::addTab (const String& tabName,
TabInfo* newTab = new TabInfo();
newTab->name = tabName;
newTab->colour = tabBackgroundColour;
newTab->component = createTabButton (tabName, insertIndex);
jassert (newTab->component != nullptr);
newTab->button = createTabButton (tabName, insertIndex);
jassert (newTab->button != nullptr);
tabs.insert (insertIndex, newTab);
currentTabIndex = tabs.indexOf (currentTab);
addAndMakeVisible (newTab->component, insertIndex);
addAndMakeVisible (newTab->button, insertIndex);
resized();
@ -232,7 +289,7 @@ void TabbedButtonBar::setTabName (const int tabIndex, const String& newName)
if (tab != nullptr && tab->name != newName)
{
tab->name = newName;
tab->component->setButtonText (newName);
tab->button->setButtonText (newName);
resized();
}
}
@ -288,7 +345,7 @@ void TabbedButtonBar::setCurrentTabIndex (int newIndex, const bool sendChangeMes
for (int i = 0; i < tabs.size(); ++i)
{
TabBarButton* tb = tabs.getUnchecked(i)->component;
TabBarButton* tb = tabs.getUnchecked(i)->button;
tb->setToggleState (i == newIndex, false);
}
@ -304,13 +361,13 @@ void TabbedButtonBar::setCurrentTabIndex (int newIndex, const bool sendChangeMes
TabBarButton* TabbedButtonBar::getTabButton (const int index) const
{
TabInfo* const tab = tabs[index];
return tab == nullptr ? nullptr : static_cast <TabBarButton*> (tab->component);
return tab == nullptr ? nullptr : static_cast <TabBarButton*> (tab->button);
}
int TabbedButtonBar::indexOfTabButton (const TabBarButton* button) const
{
for (int i = tabs.size(); --i >= 0;)
if (tabs.getUnchecked(i)->component == button)
if (tabs.getUnchecked(i)->button == button)
return i;
return -1;
@ -329,17 +386,17 @@ void TabbedButtonBar::resized()
int depth = getWidth();
int length = getHeight();
if (orientation == TabsAtTop || orientation == TabsAtBottom)
if (! isVertical())
std::swap (depth, length);
const int overlap = lf.getTabButtonOverlap (depth) + lf.getTabButtonSpaceAroundImage() * 2;
int i, totalLength = overlap;
int totalLength = overlap;
int numVisibleButtons = tabs.size();
for (i = 0; i < tabs.size(); ++i)
for (int i = 0; i < tabs.size(); ++i)
{
TabBarButton* const tb = tabs.getUnchecked(i)->component;
TabBarButton* const tb = tabs.getUnchecked(i)->button;
totalLength += tb->getBestTabLength (depth) - overlap;
tb->overlapPixels = overlap / 2;
@ -366,22 +423,22 @@ void TabbedButtonBar::resized()
const int buttonSize = jmin (proportionOfWidth (0.7f), proportionOfHeight (0.7f));
extraTabsButton->setSize (buttonSize, buttonSize);
if (orientation == TabsAtTop || orientation == TabsAtBottom)
{
tabsButtonPos = getWidth() - buttonSize / 2 - 1;
extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2);
}
else
if (isVertical())
{
tabsButtonPos = getHeight() - buttonSize / 2 - 1;
extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos);
}
else
{
tabsButtonPos = getWidth() - buttonSize / 2 - 1;
extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2);
}
totalLength = 0;
for (i = 0; i < tabs.size(); ++i)
for (int i = 0; i < tabs.size(); ++i)
{
TabBarButton* const tb = tabs.getUnchecked(i)->component;
TabBarButton* const tb = tabs.getUnchecked(i)->button;
const int newLength = totalLength + tb->getBestTabLength (depth);
@ -406,7 +463,7 @@ void TabbedButtonBar::resized()
TabBarButton* frontTab = nullptr;
for (i = 0; i < tabs.size(); ++i)
for (int i = 0; i < tabs.size(); ++i)
{
TabBarButton* const tb = getTabButton (i);
@ -416,10 +473,10 @@ void TabbedButtonBar::resized()
if (i < numVisibleButtons)
{
if (orientation == TabsAtTop || orientation == TabsAtBottom)
tb->setBounds (pos, 0, bestLength, getHeight());
else
if (isVertical())
tb->setBounds (0, pos, getWidth(), bestLength);
else
tb->setBounds (pos, 0, bestLength, getHeight());
tb->toBack();
@ -478,7 +535,7 @@ void TabbedButtonBar::showExtraItemsMenu()
{
const TabInfo* const tab = tabs.getUnchecked(i);
if (! tab->component->isVisible())
if (! tab->button->isVisible())
m.addItem (i + 1, tab->name, true, i == currentTabIndex);
}

View file

@ -49,6 +49,47 @@ public:
/** Destructor. */
~TabBarButton();
/** Returns the bar that contains this button. */
TabbedButtonBar& getTabbedButtonBar() const { return owner; }
//==============================================================================
/** When adding an extra component to the tab, this indicates which side of
the text it should be placed on. */
enum ExtraComponentPlacement
{
beforeText,
afterText
};
/** Sets an extra component that will be shown in the tab.
This optional component will be positioned inside the tab, either to the left or right
of the text. You could use this to implement things like a close button or a graphical
status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
ownership of it and delete it when required.
*/
void setExtraComponent (Component* extraTabComponent,
ExtraComponentPlacement extraComponentPlacement);
/** Returns an area of the component that's safe to draw in.
This deals with the orientation of the tabs, which affects which side is
touching the tabbed box's content component.
*/
Rectangle<int> getActiveArea() const;
/** Returns the area of the component that should contain its text. */
Rectangle<int> getTextArea() const;
/** Returns this tab's index in its tab bar. */
int getIndex() const;
/** Returns the colour of the tab. */
Colour getTabBackgroundColour() const;
/** Returns true if this is the frontmost (selected) tab. */
bool isFrontTab() const;
//==============================================================================
/** Chooses the best length for the tab, given the specified depth.
@ -59,28 +100,24 @@ public:
virtual int getBestTabLength (int depth);
//==============================================================================
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown);
void clicked (const ModifierKeys& mods);
void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown);
void clicked (const ModifierKeys&);
bool hitTest (int x, int y);
void resized();
void childBoundsChanged (Component*);
protected:
//==============================================================================
friend class TabbedButtonBar;
TabbedButtonBar& owner;
int overlapPixels;
DropShadowEffect shadow;
int overlapPixels;
/** Returns an area of the component that's safe to draw in.
This deals with the orientation of the tabs, which affects which side is
touching the tabbed box's content component.
*/
Rectangle<int> getActiveArea();
/** Returns this tab's index in its tab bar. */
int getIndex() const;
ScopedPointer<Component> extraComponent;
ExtraComponentPlacement extraCompPlacement;
private:
void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton);
};
@ -135,11 +172,16 @@ public:
*/
void setOrientation (Orientation orientation);
/** Returns the current orientation.
/** Returns the bar's current orientation.
@see setOrientation
*/
Orientation getOrientation() const noexcept { return orientation; }
Orientation getOrientation() const noexcept { return orientation; }
/** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
bool isVertical() const noexcept { return orientation == TabsAtLeft || orientation == TabsAtRight; }
/** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
int getThickness() const noexcept { return isVertical() ? getWidth() : getHeight(); }
/** Changes the minimum scale factor to which the tabs can be compressed when trying to
fit a lot of tabs on-screen.
@ -154,14 +196,12 @@ public:
void clearTabs();
/** Adds a tab to the bar.
Tabs are added in left-to-right reading order.
If this is the first tab added, it'll also be automatically selected.
*/
void addTab (const String& tabName,
const Colour& tabBackgroundColour,
int insertIndex = -1);
int insertIndex);
/** Changes the name of one of the tabs. */
void setTabName (int tabIndex,
@ -280,7 +320,7 @@ private:
struct TabInfo
{
ScopedPointer<TabBarButton> component;
ScopedPointer<TabBarButton> button;
String name;
Colour colour;
};

View file

@ -2079,60 +2079,64 @@ int LookAndFeel::getTabButtonSpaceAroundImage()
return 4;
}
void LookAndFeel::createTabButtonShape (Path& p, int width, int height, int /*tabIndex*/,
const String& /*text*/, Button& /*button*/, TabbedButtonBar::Orientation orientation,
const bool /*isMouseOver*/, const bool /*isMouseDown*/, const bool /*isFrontTab*/)
int LookAndFeel::getTabButtonBestWidth (TabBarButton& button, int tabDepth)
{
const float w = (float) width;
const float h = (float) height;
return Font (tabDepth * 0.6f).getStringWidth (button.getButtonText().trim())
+ getTabButtonOverlap (tabDepth) * 2;
}
void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool isMouseOver, bool isMouseDown)
{
const Rectangle<int> activeArea (button.getActiveArea());
const float w = (float) activeArea.getWidth();
const float h = (float) activeArea.getHeight();
float length = w;
float depth = h;
if (orientation == TabbedButtonBar::TabsAtLeft
|| orientation == TabbedButtonBar::TabsAtRight)
{
if (button.getTabbedButtonBar().isVertical())
std::swap (length, depth);
}
const float indent = (float) getTabButtonOverlap ((int) depth);
const float overhang = 4.0f;
if (orientation == TabbedButtonBar::TabsAtLeft)
switch (button.getTabbedButtonBar().getOrientation())
{
p.startNewSubPath (w, 0.0f);
p.lineTo (0.0f, indent);
p.lineTo (0.0f, h - indent);
p.lineTo (w, h);
p.lineTo (w + overhang, h + overhang);
p.lineTo (w + overhang, -overhang);
}
else if (orientation == TabbedButtonBar::TabsAtRight)
{
p.startNewSubPath (0.0f, 0.0f);
p.lineTo (w, indent);
p.lineTo (w, h - indent);
p.lineTo (0.0f, h);
p.lineTo (-overhang, h + overhang);
p.lineTo (-overhang, -overhang);
}
else if (orientation == TabbedButtonBar::TabsAtBottom)
{
p.startNewSubPath (0.0f, 0.0f);
p.lineTo (indent, h);
p.lineTo (w - indent, h);
p.lineTo (w, 0.0f);
p.lineTo (w + overhang, -overhang);
p.lineTo (-overhang, -overhang);
}
else
{
p.startNewSubPath (0.0f, h);
p.lineTo (indent, 0.0f);
p.lineTo (w - indent, 0.0f);
p.lineTo (w, h);
p.lineTo (w + overhang, h + overhang);
p.lineTo (-overhang, h + overhang);
case TabbedButtonBar::TabsAtLeft:
p.startNewSubPath (w, 0.0f);
p.lineTo (0.0f, indent);
p.lineTo (0.0f, h - indent);
p.lineTo (w, h);
p.lineTo (w + overhang, h + overhang);
p.lineTo (w + overhang, -overhang);
break;
case TabbedButtonBar::TabsAtRight:
p.startNewSubPath (0.0f, 0.0f);
p.lineTo (w, indent);
p.lineTo (w, h - indent);
p.lineTo (0.0f, h);
p.lineTo (-overhang, h + overhang);
p.lineTo (-overhang, -overhang);
break;
case TabbedButtonBar::TabsAtBottom:
p.startNewSubPath (0.0f, 0.0f);
p.lineTo (indent, h);
p.lineTo (w - indent, h);
p.lineTo (w, 0.0f);
p.lineTo (w + overhang, -overhang);
p.lineTo (-overhang, -overhang);
break;
default:
p.startNewSubPath (0.0f, h);
p.lineTo (indent, 0.0f);
p.lineTo (w - indent, 0.0f);
p.lineTo (w, h);
p.lineTo (w + overhang, h + overhang);
p.lineTo (-overhang, h + overhang);
break;
}
p.closeSubPath();
@ -2140,13 +2144,13 @@ void LookAndFeel::createTabButtonShape (Path& p, int width, int height, int /*ta
p = p.createPathWithRoundedCorners (3.0f);
}
void LookAndFeel::fillTabButtonShape (Graphics& g, const Path& path, const Colour& preferredColour,
int /*tabIndex*/, const String& /*text*/, Button& button,
TabbedButtonBar::Orientation /*orientation*/, const bool /*isMouseOver*/,
const bool /*isMouseDown*/, const bool isFrontTab)
void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool isMouseOver, bool isMouseDown)
{
g.setColour (isFrontTab ? preferredColour
: preferredColour.withMultipliedAlpha (0.9f));
const Colour tabBackground (button.getTabBackgroundColour());
const bool isFrontTab = button.isFrontTab();
g.setColour (isFrontTab ? tabBackground
: tabBackground.withMultipliedAlpha (0.9f));
g.fillPath (path);
@ -2157,44 +2161,44 @@ void LookAndFeel::fillTabButtonShape (Graphics& g, const Path& path, const Colou
g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f));
}
void LookAndFeel::drawTabButtonText (Graphics& g, int x, int y, int w, int h,
const Colour& preferredBackgroundColour, int /*tabIndex*/,
const String& text, Button& button, TabbedButtonBar::Orientation orientation,
const bool isMouseOver, const bool isMouseDown, const bool isFrontTab)
void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
{
int length = w;
int depth = h;
const Rectangle<float> area (button.getTextArea().toFloat());
if (orientation == TabbedButtonBar::TabsAtLeft
|| orientation == TabbedButtonBar::TabsAtRight)
{
int length = area.getWidth();
int depth = area.getHeight();
if (button.getTabbedButtonBar().isVertical())
std::swap (length, depth);
}
Font font (depth * 0.6f);
font.setUnderline (button.hasKeyboardFocus (false));
GlyphArrangement textLayout;
textLayout.addFittedText (font, text.trim(),
textLayout.addFittedText (font, button.getButtonText().trim(),
0.0f, 0.0f, (float) length, (float) depth,
Justification::centred,
jmax (1, depth / 12));
AffineTransform t;
switch (orientation)
switch (button.getTabbedButtonBar().getOrientation())
{
case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated ((float) x, (float) (y + h)); break;
case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated ((float) (x + w), (float) y); break;
default: t = t.translated ((float) x, (float) y);
case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break;
case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break;
case TabbedButtonBar::TabsAtTop:
case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break;
default: jassertfalse; break;
}
if (isFrontTab && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) || isColourSpecified (TabbedButtonBar::frontTextColourId)))
if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId)
|| isColourSpecified (TabbedButtonBar::frontTextColourId)))
g.setColour (findColour (TabbedButtonBar::frontTextColourId));
else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) || isColourSpecified (TabbedButtonBar::tabTextColourId))
else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId)
|| isColourSpecified (TabbedButtonBar::tabTextColourId))
g.setColour (findColour (TabbedButtonBar::tabTextColourId));
else
g.setColour (preferredBackgroundColour.contrasting());
g.setColour (button.getTabBackgroundColour().contrasting());
if (! (isMouseOver || isMouseDown))
g.setOpacity (0.8f);
@ -2205,85 +2209,60 @@ void LookAndFeel::drawTabButtonText (Graphics& g, int x, int y, int w, int h,
textLayout.draw (g, t);
}
int LookAndFeel::getTabButtonBestWidth (int /*tabIndex*/, const String& text, int tabDepth, Button&)
{
Font f (tabDepth * 0.6f);
return f.getStringWidth (text.trim()) + getTabButtonOverlap (tabDepth) * 2;
}
void LookAndFeel::drawTabButton (Graphics& g, int w, int h, const Colour& preferredColour,
int tabIndex, const String& text, Button& button, TabbedButtonBar::Orientation orientation,
const bool isMouseOver, const bool isMouseDown, const bool isFrontTab)
void LookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
{
Path tabShape;
createTabButtonShape (tabShape, w, h, tabIndex, text, button, orientation,
isMouseOver, isMouseDown, isFrontTab);
createTabButtonShape (button, tabShape, isMouseOver, isMouseDown);
fillTabButtonShape (g, tabShape, preferredColour,
tabIndex, text, button, orientation,
isMouseOver, isMouseDown, isFrontTab);
const Rectangle<int> activeArea (button.getActiveArea());
tabShape.applyTransform (AffineTransform::translation ((float) activeArea.getX(),
(float) activeArea.getY()));
const int depth = (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight) ? w : h;
const int indent = getTabButtonOverlap (depth);
int x = 0, y = 0;
fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown);
if (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight)
{
y += indent;
h -= indent * 2;
}
else
{
x += indent;
w -= indent * 2;
}
drawTabButtonText (g, x, y, w, h, preferredColour,
tabIndex, text, button, orientation,
isMouseOver, isMouseDown, isFrontTab);
drawTabButtonText (button, g, isMouseOver, isMouseDown);
}
void LookAndFeel::drawTabAreaBehindFrontButton (Graphics& g, int w, int h, TabbedButtonBar& tabBar,
TabbedButtonBar::Orientation orientation)
void LookAndFeel::drawTabAreaBehindFrontButton (TabbedButtonBar& bar, Graphics& g, const int w, const int h)
{
const float shadowSize = 0.2f;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
Rectangle<int> shadowRect, line;
ColourGradient gradient (Colours::black.withAlpha (bar.isEnabled() ? 0.3f : 0.15f), 0, 0,
Colours::transparentBlack, 0, 0, false);
switch (orientation)
switch (bar.getOrientation())
{
case TabbedButtonBar::TabsAtLeft:
x1 = (float) w;
x2 = w * (1.0f - shadowSize);
shadowRect.setBounds ((int) x2, 0, w - (int) x2, h);
gradient.point1.x = (float) w;
gradient.point2.x = w * (1.0f - shadowSize);
shadowRect.setBounds ((int) gradient.point2.x, 0, w - (int) gradient.point2.x, h);
line.setBounds (w - 1, 0, 1, h);
break;
case TabbedButtonBar::TabsAtRight:
x2 = w * shadowSize;
shadowRect.setBounds (0, 0, (int) x2, h);
gradient.point2.x = w * shadowSize;
shadowRect.setBounds (0, 0, (int) gradient.point2.x, h);
line.setBounds (0, 0, 1, h);
break;
case TabbedButtonBar::TabsAtTop:
y1 = (float) h;
y2 = h * (1.0f - shadowSize);
shadowRect.setBounds (0, (int) y2, w, h - (int) y2);
gradient.point1.y = (float) h;
gradient.point2.y = h * (1.0f - shadowSize);
shadowRect.setBounds (0, (int) gradient.point2.y, w, h - (int) gradient.point2.y);
line.setBounds (0, h - 1, w, 1);
break;
case TabbedButtonBar::TabsAtBottom:
y2 = h * shadowSize;
shadowRect.setBounds (0, 0, w, (int) y2);
gradient.point2.y = h * shadowSize;
shadowRect.setBounds (0, 0, w, (int) gradient.point2.y);
line.setBounds (0, 0, w, 1);
break;
default: break;
}
g.setGradientFill (ColourGradient (Colours::black.withAlpha (tabBar.isEnabled() ? 0.3f : 0.15f), x1, y1,
Colours::transparentBlack, x2, y2, false));
g.setGradientFill (gradient);
g.fillRect (shadowRect.expanded (2, 2));
g.setColour (Colour (0x80000000));

View file

@ -513,61 +513,16 @@ public:
GroupComponent& group);
//==============================================================================
virtual void createTabButtonShape (Path& p,
int width, int height,
int tabIndex,
const String& text,
Button& button,
TabbedButtonBar::Orientation orientation,
bool isMouseOver,
bool isMouseDown,
bool isFrontTab);
virtual void fillTabButtonShape (Graphics& g,
const Path& path,
const Colour& preferredBackgroundColour,
int tabIndex,
const String& text,
Button& button,
TabbedButtonBar::Orientation orientation,
bool isMouseOver,
bool isMouseDown,
bool isFrontTab);
virtual void drawTabButtonText (Graphics& g,
int x, int y, int w, int h,
const Colour& preferredBackgroundColour,
int tabIndex,
const String& text,
Button& button,
TabbedButtonBar::Orientation orientation,
bool isMouseOver,
bool isMouseDown,
bool isFrontTab);
virtual int getTabButtonOverlap (int tabDepth);
virtual int getTabButtonSpaceAroundImage();
virtual int getTabButtonOverlap (int tabDepth);
virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth);
virtual int getTabButtonBestWidth (int tabIndex,
const String& text,
int tabDepth,
Button& button);
virtual void drawTabButton (TabBarButton&, Graphics& g, bool isMouseOver, bool isMouseDown);
virtual void drawTabButtonText (TabBarButton&, Graphics& g, bool isMouseOver, bool isMouseDown);
virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics& g, int w, int h);
virtual void drawTabButton (Graphics& g,
int w, int h,
const Colour& preferredColour,
int tabIndex,
const String& text,
Button& button,
TabbedButtonBar::Orientation orientation,
bool isMouseOver,
bool isMouseDown,
bool isFrontTab);
virtual void drawTabAreaBehindFrontButton (Graphics& g,
int w, int h,
TabbedButtonBar& tabBar,
TabbedButtonBar::Orientation orientation);
virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown);
virtual void fillTabButtonShape (TabBarButton&, Graphics& g, const Path& path, bool isMouseOver, bool isMouseDown);
virtual Button* createTabBarExtrasButton();
@ -673,8 +628,16 @@ private:
bool flatOnTop,
bool flatOnBottom) noexcept;
// This has been deprecated - see the new parameter list..
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// These methods have been deprecated - see their new parameter lists..
virtual int drawFileBrowserRow (Graphics&, int, int, const String&, Image*, const String&, const String&, bool, bool, int) { return 0; }
virtual int drawTabButton (Graphics&, int, int, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; }
virtual int createTabButtonShape (Path&, int, int, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; }
virtual int fillTabButtonShape (Graphics&, const Path&, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; }
virtual int drawTabAreaBehindFrontButton (Graphics&, int, int, TabbedButtonBar&, TabbedButtonBar::Orientation) { return 0; }
virtual int drawTabButtonText (Graphics&, int, int, int, int, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; }
virtual int getTabButtonBestWidth (int, const String&, int, Button&) { return 0; }
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel);
};