From 591ce2a396fe2f92354cb11fb075803bfa95a45a Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 11 Jul 2012 15:12:29 +0100 Subject: [PATCH] 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. --- extras/JuceDemo/Source/demos/WidgetsDemo.cpp | 259 +++++++++--------- .../layout/juce_TabbedButtonBar.cpp | 183 ++++++++----- .../layout/juce_TabbedButtonBar.h | 80 ++++-- .../lookandfeel/juce_LookAndFeel.cpp | 207 +++++++------- .../lookandfeel/juce_LookAndFeel.h | 69 ++--- 5 files changed, 412 insertions(+), 386 deletions(-) diff --git a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp index 5c9a565ca6..f399808a6b 100644 --- a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp +++ b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp @@ -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 ballBounds; + Point 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 parent; // A safe-pointer will become zero if the component that it refers to is deleted.. + Component::SafePointer 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 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(), 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(); + + 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' diff --git a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp index b7ff56ba60..12770455dc 100644 --- a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp +++ b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp @@ -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 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 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 TabBarButton::getActiveArea() +void TabBarButton::calcAreas (Rectangle& extraComp, Rectangle& 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 TabBarButton::getTextArea() const +{ + Rectangle extraComp, text; + calcAreas (extraComp, text); + return text; +} + +Rectangle TabBarButton::getActiveArea() const { Rectangle 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 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 (tab->component); + return tab == nullptr ? nullptr : static_cast (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); } diff --git a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h index ecc3e4cf2d..b1e1ca63a6 100644 --- a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h +++ b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h @@ -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 getActiveArea() const; + + /** Returns the area of the component that should contain its text. */ + Rectangle 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 getActiveArea(); - - /** Returns this tab's index in its tab bar. */ - int getIndex() const; + ScopedPointer extraComponent; + ExtraComponentPlacement extraCompPlacement; private: + void calcAreas (Rectangle&, Rectangle&) 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 component; + ScopedPointer button; String name; Colour colour; }; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp index 96c21b8642..65ff818a72 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp @@ -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 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 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 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 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)); diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h index ace3831161..a4c53a9e47 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h @@ -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); };