| @@ -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; | |||
| if (x < 0) | |||
| dx = fabsf (dx); | |||
| if (x > getParentWidth()) | |||
| dx = -fabsf (dx); | |||
| ballBounds += direction; | |||
| if (y < 0) | |||
| dy = fabsf (dy); | |||
| 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 (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); | |||
| CallOutBox callOut (colourSelector, *this, 0); | |||
| callOut.runModalLoop(); | |||
| #endif | |||
| 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::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' | |||
| @@ -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); | |||
| } | |||
| 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() | |||
| 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) | |||
| if (isVertical()) | |||
| { | |||
| tabsButtonPos = getWidth() - buttonSize / 2 - 1; | |||
| extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2); | |||
| tabsButtonPos = getHeight() - buttonSize / 2 - 1; | |||
| extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos); | |||
| } | |||
| else | |||
| { | |||
| tabsButtonPos = getHeight() - buttonSize / 2 - 1; | |||
| extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos); | |||
| 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); | |||
| } | |||
| @@ -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; | |||
| }; | |||
| @@ -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) | |||
| { | |||
| 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) | |||
| switch (button.getTabbedButtonBar().getOrientation()) | |||
| { | |||
| 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; | |||
| if (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight) | |||
| { | |||
| y += indent; | |||
| h -= indent * 2; | |||
| } | |||
| else | |||
| { | |||
| x += indent; | |||
| w -= indent * 2; | |||
| } | |||
| fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); | |||
| 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)); | |||
| @@ -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 (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 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 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); | |||
| }; | |||