The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1503 lines
55KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-12 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../JuceDemoHeader.h"
  19. static void showBubbleMessage (Component* targetComponent, const String& textToShow)
  20. {
  21. BubbleMessageComponent* bmc = new BubbleMessageComponent();
  22. if (Desktop::canUseSemiTransparentWindows())
  23. {
  24. bmc->setAlwaysOnTop (true);
  25. bmc->addToDesktop (0);
  26. }
  27. else
  28. {
  29. targetComponent->getTopLevelComponent()->addChildComponent (bmc);
  30. }
  31. AttributedString text (textToShow);
  32. text.setJustification (Justification::centred);
  33. bmc->showAt (targetComponent, text, 2000, true, true);
  34. }
  35. //==============================================================================
  36. /** To demonstrate how sliders can have custom snapping applied to their values,
  37. this simple class snaps the value to 50 if it comes near.
  38. */
  39. struct SnappingSlider : public Slider
  40. {
  41. double snapValue (double attemptedValue, bool userIsDragging)
  42. {
  43. if (! userIsDragging)
  44. return attemptedValue; // if they're entering the value in the text-box, don't mess with it.
  45. if (attemptedValue > 40 && attemptedValue < 60)
  46. return 50.0;
  47. else
  48. return attemptedValue;
  49. }
  50. };
  51. /** A TextButton that pops up a colour chooser to change its colours. */
  52. class ColourChangeButton : public TextButton,
  53. public ChangeListener
  54. {
  55. public:
  56. ColourChangeButton()
  57. : TextButton ("Click to change colour...")
  58. {
  59. setSize (10, 24);
  60. changeWidthToFitText();
  61. }
  62. void clicked() override
  63. {
  64. ColourSelector* colourSelector = new ColourSelector();
  65. colourSelector->setName ("background");
  66. colourSelector->setCurrentColour (findColour (TextButton::buttonColourId));
  67. colourSelector->addChangeListener (this);
  68. colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
  69. colourSelector->setSize (300, 400);
  70. CallOutBox::launchAsynchronously (colourSelector, getScreenBounds(), nullptr);
  71. }
  72. void changeListenerCallback (ChangeBroadcaster* source)
  73. {
  74. if (ColourSelector* cs = dynamic_cast <ColourSelector*> (source))
  75. setColour (TextButton::buttonColourId, cs->getCurrentColour());
  76. }
  77. };
  78. //==============================================================================
  79. struct SlidersPage : public Component
  80. {
  81. SlidersPage()
  82. : hintLabel ("hint", "Try right-clicking on a slider for an options menu. \n\n"
  83. "Also, holding down CTRL while dragging will turn on a slider's velocity-sensitive mode")
  84. {
  85. Slider* s = createSlider (false);
  86. s->setSliderStyle (Slider::LinearVertical);
  87. s->setTextBoxStyle (Slider::TextBoxBelow, false, 100, 20);
  88. s->setBounds (10, 25, 70, 200);
  89. s->setDoubleClickReturnValue (true, 50.0); // double-clicking this slider will set it to 50.0
  90. s->setTextValueSuffix (" units");
  91. s = createSlider (false);
  92. s->setSliderStyle (Slider::LinearVertical);
  93. s->setVelocityBasedMode (true);
  94. s->setSkewFactor (0.5);
  95. s->setTextBoxStyle (Slider::TextBoxAbove, true, 100, 20);
  96. s->setBounds (85, 25, 70, 200);
  97. s->setTextValueSuffix (" rels");
  98. s = createSlider (true);
  99. s->setSliderStyle (Slider::LinearHorizontal);
  100. s->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
  101. s->setBounds (180, 35, 150, 20);
  102. s = createSlider (false);
  103. s->setSliderStyle (Slider::LinearHorizontal);
  104. s->setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
  105. s->setBounds (180, 65, 150, 20);
  106. s->setPopupDisplayEnabled (true, this);
  107. s->setTextValueSuffix (" nuns required to change a lightbulb");
  108. s = createSlider (false);
  109. s->setSliderStyle (Slider::IncDecButtons);
  110. s->setTextBoxStyle (Slider::TextBoxLeft, false, 50, 20);
  111. s->setBounds (180, 105, 100, 20);
  112. s->setIncDecButtonsMode (Slider::incDecButtonsDraggable_Vertical);
  113. s = createSlider (false);
  114. s->setSliderStyle (Slider::Rotary);
  115. s->setRotaryParameters (float_Pi * 1.2f, float_Pi * 2.8f, false);
  116. s->setTextBoxStyle (Slider::TextBoxRight, false, 70, 20);
  117. s->setBounds (190, 145, 120, 40);
  118. s->setTextValueSuffix (" mm");
  119. s = createSlider (false);
  120. s->setSliderStyle (Slider::LinearBar);
  121. s->setBounds (180, 195, 100, 30);
  122. s->setTextValueSuffix (" gallons");
  123. s = createSlider (false);
  124. s->setSliderStyle (Slider::TwoValueHorizontal);
  125. s->setBounds (360, 20, 160, 40);
  126. s = createSlider (false);
  127. s->setSliderStyle (Slider::TwoValueVertical);
  128. s->setBounds (360, 110, 40, 160);
  129. s = createSlider (false);
  130. s->setSliderStyle (Slider::ThreeValueHorizontal);
  131. s->setBounds (360, 70, 160, 40);
  132. s = createSlider (false);
  133. s->setSliderStyle (Slider::ThreeValueVertical);
  134. s->setBounds (440, 110, 40, 160);
  135. s = createSlider (false);
  136. s->setSliderStyle (Slider::LinearBarVertical);
  137. s->setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
  138. s->setBounds (540, 35, 20, 230);
  139. s->setPopupDisplayEnabled (true, this);
  140. s->setTextValueSuffix (" mickles in a muckle");
  141. for (int i = 7; i <= 10; ++i)
  142. {
  143. sliders.getUnchecked(i)->setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
  144. sliders.getUnchecked(i)->setPopupDisplayEnabled (true, this);
  145. }
  146. /* Here, we'll create a Value object, and tell a bunch of our sliders to use it as their
  147. value source. By telling them all to share the same Value, they'll stay in sync with
  148. each other.
  149. We could also optionally keep a copy of this Value elsewhere, and by changing it,
  150. cause all the sliders to automatically update.
  151. */
  152. Value sharedValue;
  153. sharedValue = Random::getSystemRandom().nextDouble() * 100;
  154. for (int i = 0; i < 7; ++i)
  155. sliders.getUnchecked(i)->getValueObject().referTo (sharedValue);
  156. // ..and now we'll do the same for all our min/max slider values..
  157. Value sharedValueMin, sharedValueMax;
  158. sharedValueMin = Random::getSystemRandom().nextDouble() * 40.0;
  159. sharedValueMax = Random::getSystemRandom().nextDouble() * 40.0 + 60.0;
  160. for (int i = 7; i <= 10; ++i)
  161. {
  162. sliders.getUnchecked(i)->getMaxValueObject().referTo (sharedValueMax);
  163. sliders.getUnchecked(i)->getMinValueObject().referTo (sharedValueMin);
  164. }
  165. hintLabel.setBounds (20, 245, 350, 150);
  166. addAndMakeVisible (&hintLabel);
  167. }
  168. private:
  169. OwnedArray<Slider> sliders;
  170. Label hintLabel;
  171. Slider* createSlider (bool isSnapping)
  172. {
  173. Slider* s = isSnapping ? new SnappingSlider() : new Slider();
  174. sliders.add (s);
  175. addAndMakeVisible (s);
  176. s->setRange (0.0, 100.0, 0.1);
  177. s->setPopupMenuEnabled (true);
  178. s->setValue (Random::getSystemRandom().nextDouble() * 100.0, dontSendNotification);
  179. return s;
  180. }
  181. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlidersPage)
  182. };
  183. //==============================================================================
  184. struct ButtonsPage : public Component,
  185. public ButtonListener
  186. {
  187. ButtonsPage()
  188. {
  189. {
  190. GroupComponent* group = addToList (new GroupComponent ("group", "Radio buttons"));
  191. group->setBounds (20, 20, 220, 140);
  192. }
  193. for (int i = 0; i < 4; ++i)
  194. {
  195. ToggleButton* tb = addToList (new ToggleButton ("Radio Button #" + String (i + 1)));
  196. tb->setRadioGroupId (1234);
  197. tb->setBounds (45, 46 + i * 22, 180, 22);
  198. tb->setTooltip ("A set of mutually-exclusive radio buttons");
  199. if (i == 0)
  200. tb->setToggleState (true, dontSendNotification);
  201. }
  202. for (int i = 0; i < 4; ++i)
  203. {
  204. DrawablePath normal, over;
  205. Path p;
  206. p.addStar (Point<float>(), i + 5, 20.0f, 50.0f, -0.2f);
  207. normal.setPath (p);
  208. normal.setFill (Colours::lightblue);
  209. normal.setStrokeFill (Colours::black);
  210. normal.setStrokeThickness (4.0f);
  211. over.setPath (p);
  212. over.setFill (Colours::blue);
  213. over.setStrokeFill (Colours::black);
  214. over.setStrokeThickness (4.0f);
  215. DrawableButton* db = addToList (new DrawableButton (String (i + 5) + " points", DrawableButton::ImageAboveTextLabel));
  216. db->setImages (&normal, &over, 0);
  217. db->setClickingTogglesState (true);
  218. db->setRadioGroupId (23456);
  219. const int buttonSize = 50;
  220. db->setBounds (25 + i * buttonSize, 180, buttonSize, buttonSize);
  221. if (i == 0)
  222. db->setToggleState (true, dontSendNotification);
  223. }
  224. for (int i = 0; i < 4; ++i)
  225. {
  226. TextButton* tb = addToList (new TextButton ("Button " + String (i + 1)));
  227. tb->setClickingTogglesState (true);
  228. tb->setRadioGroupId (34567);
  229. tb->setColour (TextButton::buttonColourId, Colours::white);
  230. tb->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter());
  231. tb->setBounds (20 + i * 55, 260, 55, 24);
  232. tb->setConnectedEdges (((i != 0) ? Button::ConnectedOnLeft : 0)
  233. | ((i != 3) ? Button::ConnectedOnRight : 0));
  234. if (i == 0)
  235. tb->setToggleState (true, dontSendNotification);
  236. }
  237. {
  238. ColourChangeButton* colourChangeButton = new ColourChangeButton();
  239. components.add (colourChangeButton);
  240. addAndMakeVisible (colourChangeButton);
  241. colourChangeButton->setTopLeftPosition (20, 320);
  242. }
  243. {
  244. HyperlinkButton* hyperlink = addToList (new HyperlinkButton ("This is a HyperlinkButton",
  245. URL ("http://www.juce.com")));
  246. hyperlink->setBounds (260, 20, 200, 24);
  247. }
  248. // create some drawables to use for our drawable buttons...
  249. DrawablePath normal, over;
  250. {
  251. Path p;
  252. p.addStar (Point<float>(), 5, 20.0f, 50.0f, 0.2f);
  253. normal.setPath (p);
  254. normal.setFill (getRandomDarkColour());
  255. }
  256. {
  257. Path p;
  258. p.addStar (Point<float>(), 9, 25.0f, 50.0f, 0.0f);
  259. over.setPath (p);
  260. over.setFill (getRandomBrightColour());
  261. over.setStrokeFill (getRandomDarkColour());
  262. over.setStrokeThickness (5.0f);
  263. }
  264. DrawableImage down;
  265. down.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize));
  266. down.setOverlayColour (Colours::black.withAlpha (0.3f));
  267. {
  268. // create an image-above-text button from these drawables..
  269. DrawableButton* db = addToList (new DrawableButton ("Button 1", DrawableButton::ImageAboveTextLabel));
  270. db->setImages (&normal, &over, &down);
  271. db->setBounds (260, 60, 80, 80);
  272. db->setTooltip ("This is a DrawableButton with a label");
  273. db->addListener (this);
  274. }
  275. {
  276. // create an image-only button from these drawables..
  277. DrawableButton* db = addToList (new DrawableButton ("Button 2", DrawableButton::ImageFitted));
  278. db->setImages (&normal, &over, &down);
  279. db->setClickingTogglesState (true);
  280. db->setBounds (370, 60, 80, 80);
  281. db->setTooltip ("This is an image-only DrawableButton");
  282. db->addListener (this);
  283. }
  284. {
  285. // create an image-on-button-shape button from the same drawables..
  286. DrawableButton* db = addToList (new DrawableButton ("Button 3", DrawableButton::ImageOnButtonBackground));
  287. db->setImages (&normal, 0, 0);
  288. db->setBounds (260, 160, 110, 25);
  289. db->setTooltip ("This is a DrawableButton on a standard button background");
  290. db->addListener (this);
  291. }
  292. {
  293. DrawableButton* db = addToList (new DrawableButton ("Button 4", DrawableButton::ImageOnButtonBackground));
  294. db->setImages (&normal, &over, &down);
  295. db->setClickingTogglesState (true);
  296. db->setColour (DrawableButton::backgroundColourId, Colours::white);
  297. db->setColour (DrawableButton::backgroundOnColourId, Colours::yellow);
  298. db->setBounds (400, 150, 50, 50);
  299. db->setTooltip ("This is a DrawableButton on a standard button background");
  300. db->addListener (this);
  301. }
  302. {
  303. ShapeButton* sb = addToList (new ShapeButton ("ShapeButton",
  304. getRandomDarkColour(),
  305. getRandomDarkColour(),
  306. getRandomDarkColour()));
  307. sb->setShape (MainAppWindow::getJUCELogoPath(), false, true, false);
  308. sb->setBounds (260, 220, 200, 120);
  309. }
  310. {
  311. ImageButton* ib = addToList (new ImageButton ("ImageButton"));
  312. Image juceImage = ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize);
  313. ib->setImages (true, true, true,
  314. juceImage, 0.7f, Colours::transparentBlack,
  315. juceImage, 1.0f, Colours::transparentBlack,
  316. juceImage, 1.0f, getRandomBrightColour().withAlpha (0.8f),
  317. 0.5f);
  318. ib->setBounds (260, 350, 100, 100);
  319. ib->setTooltip ("ImageButton - showing alpha-channel hit-testing and colour overlay when clicked");
  320. }
  321. }
  322. private:
  323. OwnedArray<Component> components;
  324. // This little function avoids a bit of code-duplication by adding a component to
  325. // our list as well as calling addAndMakeVisible on it..
  326. template <typename ComponentType>
  327. ComponentType* addToList (ComponentType* newComp)
  328. {
  329. components.add (newComp);
  330. addAndMakeVisible (newComp);
  331. return newComp;
  332. }
  333. void buttonClicked (Button* button)
  334. {
  335. showBubbleMessage (button,
  336. "This is a demo of the BubbleMessageComponent, which lets you pop up a message pointing "
  337. "at a component or somewhere on the screen.\n\n"
  338. "The message bubbles will disappear after a timeout period, or when the mouse is clicked.");
  339. }
  340. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonsPage)
  341. };
  342. //==============================================================================
  343. struct MiscPage : public Component
  344. {
  345. MiscPage()
  346. : textEditor2 ("Password", (juce_wchar) 0x2022),
  347. comboBox ("Combo")
  348. {
  349. addAndMakeVisible (&textEditor1);
  350. textEditor1.setBounds (10, 25, 200, 24);
  351. textEditor1.setText ("Single-line text box");
  352. addAndMakeVisible (&textEditor2);
  353. textEditor2.setBounds (10, 55, 200, 24);
  354. textEditor2.setText ("Password");
  355. addAndMakeVisible (&comboBox);
  356. comboBox.setBounds (10, 85, 200, 24);
  357. comboBox.setEditableText (true);
  358. comboBox.setJustificationType (Justification::centred);
  359. for (int i = 1; i < 100; ++i)
  360. comboBox.addItem ("combo box item " + String (i), i);
  361. comboBox.setSelectedId (1);
  362. }
  363. TextEditor textEditor1, textEditor2;
  364. ComboBox comboBox;
  365. };
  366. //==============================================================================
  367. class ToolbarDemoComp : public Component,
  368. public SliderListener,
  369. public ButtonListener
  370. {
  371. public:
  372. ToolbarDemoComp()
  373. : depthLabel (String::empty, "Toolbar depth:"),
  374. infoLabel (String::empty, "As well as showing off toolbars, this demo illustrates how to store "
  375. "a set of SVG files in a Zip file, embed that in your application, and read "
  376. "them back in at runtime.\n\nThe icon images here are taken from the open-source "
  377. "Tango icon project."),
  378. orientationButton ("Vertical/Horizontal"),
  379. customiseButton ("Customise...")
  380. {
  381. // Create and add the toolbar...
  382. addAndMakeVisible (&toolbar);
  383. // And use our item factory to add a set of default icons to it...
  384. toolbar.addDefaultItems (factory);
  385. // Now we'll just create the other sliders and buttons on the demo page, which adjust
  386. // the toolbar's properties...
  387. addAndMakeVisible (&infoLabel);
  388. infoLabel.setJustificationType (Justification::topLeft);
  389. infoLabel.setBounds (80, 80, 450, 100);
  390. infoLabel.setInterceptsMouseClicks (false, false);
  391. addAndMakeVisible (&depthSlider);
  392. depthSlider.setRange (10.0, 200.0, 1.0);
  393. depthSlider.setValue (50, dontSendNotification);
  394. depthSlider.setSliderStyle (Slider::LinearHorizontal);
  395. depthSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
  396. depthSlider.addListener (this);
  397. depthSlider.setBounds (80, 210, 300, 22);
  398. depthLabel.attachToComponent (&depthSlider, false);
  399. addAndMakeVisible (&orientationButton);
  400. orientationButton.addListener (this);
  401. orientationButton.changeWidthToFitText (22);
  402. orientationButton.setTopLeftPosition (depthSlider.getX(), depthSlider.getBottom() + 20);
  403. addAndMakeVisible (&customiseButton);
  404. customiseButton.addListener (this);
  405. customiseButton.changeWidthToFitText (22);
  406. customiseButton.setTopLeftPosition (orientationButton.getRight() + 20, orientationButton.getY());
  407. }
  408. void resized()
  409. {
  410. int toolbarThickness = (int) depthSlider.getValue();
  411. if (toolbar.isVertical())
  412. toolbar.setBounds (getLocalBounds().removeFromLeft (toolbarThickness));
  413. else
  414. toolbar.setBounds (getLocalBounds().removeFromTop (toolbarThickness));
  415. }
  416. void sliderValueChanged (Slider*)
  417. {
  418. resized();
  419. }
  420. void buttonClicked (Button* button)
  421. {
  422. if (button == &orientationButton)
  423. {
  424. toolbar.setVertical (! toolbar.isVertical());
  425. resized();
  426. }
  427. else if (button == &customiseButton)
  428. {
  429. toolbar.showCustomisationDialog (factory);
  430. }
  431. }
  432. private:
  433. Toolbar toolbar;
  434. Slider depthSlider;
  435. Label depthLabel, infoLabel;
  436. TextButton orientationButton, customiseButton;
  437. //==============================================================================
  438. class DemoToolbarItemFactory : public ToolbarItemFactory
  439. {
  440. public:
  441. DemoToolbarItemFactory() {}
  442. //==============================================================================
  443. // Each type of item a toolbar can contain must be given a unique ID. These
  444. // are the ones we'll use in this demo.
  445. enum DemoToolbarItemIds
  446. {
  447. doc_new = 1,
  448. doc_open = 2,
  449. doc_save = 3,
  450. doc_saveAs = 4,
  451. edit_copy = 5,
  452. edit_cut = 6,
  453. edit_paste = 7,
  454. juceLogoButton = 8,
  455. customComboBox = 9
  456. };
  457. void getAllToolbarItemIds (Array <int>& ids)
  458. {
  459. // This returns the complete list of all item IDs that are allowed to
  460. // go in our toolbar. Any items you might want to add must be listed here. The
  461. // order in which they are listed will be used by the toolbar customisation panel.
  462. ids.add (doc_new);
  463. ids.add (doc_open);
  464. ids.add (doc_save);
  465. ids.add (doc_saveAs);
  466. ids.add (edit_copy);
  467. ids.add (edit_cut);
  468. ids.add (edit_paste);
  469. ids.add (juceLogoButton);
  470. ids.add (customComboBox);
  471. // If you're going to use separators, then they must also be added explicitly
  472. // to the list.
  473. ids.add (separatorBarId);
  474. ids.add (spacerId);
  475. ids.add (flexibleSpacerId);
  476. }
  477. void getDefaultItemSet (Array <int>& ids)
  478. {
  479. // This returns an ordered list of the set of items that make up a
  480. // toolbar's default set. Not all items need to be on this list, and
  481. // items can appear multiple times (e.g. the separators used here).
  482. ids.add (doc_new);
  483. ids.add (doc_open);
  484. ids.add (doc_save);
  485. ids.add (doc_saveAs);
  486. ids.add (spacerId);
  487. ids.add (separatorBarId);
  488. ids.add (edit_copy);
  489. ids.add (edit_cut);
  490. ids.add (edit_paste);
  491. ids.add (separatorBarId);
  492. ids.add (flexibleSpacerId);
  493. ids.add (customComboBox);
  494. ids.add (flexibleSpacerId);
  495. ids.add (separatorBarId);
  496. ids.add (juceLogoButton);
  497. }
  498. ToolbarItemComponent* createItem (int itemId)
  499. {
  500. switch (itemId)
  501. {
  502. case doc_new: return createButtonFromZipFileSVG (itemId, "new", "document-new.svg");
  503. case doc_open: return createButtonFromZipFileSVG (itemId, "open", "document-open.svg");
  504. case doc_save: return createButtonFromZipFileSVG (itemId, "save", "document-save.svg");
  505. case doc_saveAs: return createButtonFromZipFileSVG (itemId, "save as", "document-save-as.svg");
  506. case edit_copy: return createButtonFromZipFileSVG (itemId, "copy", "edit-copy.svg");
  507. case edit_cut: return createButtonFromZipFileSVG (itemId, "cut", "edit-cut.svg");
  508. case edit_paste: return createButtonFromZipFileSVG (itemId, "paste", "edit-paste.svg");
  509. case juceLogoButton: return new ToolbarButton (itemId, "juce!", Drawable::createFromImageData (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), 0);
  510. case customComboBox: return new CustomToolbarComboBox (itemId);
  511. default: break;
  512. }
  513. return 0;
  514. }
  515. private:
  516. StringArray iconNames;
  517. OwnedArray<Drawable> iconsFromZipFile;
  518. // This is a little utility to create a button with one of the SVG images in
  519. // our embedded ZIP file "icons.zip"
  520. ToolbarButton* createButtonFromZipFileSVG (const int itemId, const String& text, const String& filename)
  521. {
  522. if (iconsFromZipFile.size() == 0)
  523. {
  524. // If we've not already done so, load all the images from the zip file..
  525. MemoryInputStream iconsFileStream (BinaryData::icons_zip, BinaryData::icons_zipSize, false);
  526. ZipFile icons (&iconsFileStream, false);
  527. for (int i = 0; i < icons.getNumEntries(); ++i)
  528. {
  529. ScopedPointer<InputStream> svgFileStream (icons.createStreamForEntry (i));
  530. if (svgFileStream != 0)
  531. {
  532. iconNames.add (icons.getEntry(i)->filename);
  533. iconsFromZipFile.add (Drawable::createFromImageDataStream (*svgFileStream));
  534. }
  535. }
  536. }
  537. Drawable* image = iconsFromZipFile [iconNames.indexOf (filename)]->createCopy();
  538. return new ToolbarButton (itemId, text, image, 0);
  539. }
  540. // Demonstrates how to put a custom component into a toolbar - this one contains
  541. // a ComboBox.
  542. class CustomToolbarComboBox : public ToolbarItemComponent
  543. {
  544. public:
  545. CustomToolbarComboBox (const int toolbarItemId)
  546. : ToolbarItemComponent (toolbarItemId, "Custom Toolbar Item", false),
  547. comboBox ("demo toolbar combo box")
  548. {
  549. addAndMakeVisible (&comboBox);
  550. for (int i = 1; i < 20; ++i)
  551. comboBox.addItem ("Toolbar ComboBox item " + String (i), i);
  552. comboBox.setSelectedId (1);
  553. comboBox.setEditableText (true);
  554. }
  555. bool getToolbarItemSizes (int /*toolbarDepth*/, bool isVertical,
  556. int& preferredSize, int& minSize, int& maxSize)
  557. {
  558. if (isVertical)
  559. return false;
  560. preferredSize = 250;
  561. minSize = 80;
  562. maxSize = 300;
  563. return true;
  564. }
  565. void paintButtonArea (Graphics&, int, int, bool, bool)
  566. {
  567. }
  568. void contentAreaChanged (const Rectangle<int>& newArea)
  569. {
  570. comboBox.setSize (newArea.getWidth() - 2,
  571. jmin (newArea.getHeight() - 2, 22));
  572. comboBox.setCentrePosition (newArea.getCentreX(), newArea.getCentreY());
  573. }
  574. private:
  575. ComboBox comboBox;
  576. };
  577. };
  578. DemoToolbarItemFactory factory;
  579. };
  580. //==============================================================================
  581. /**
  582. This class shows how to implement a TableListBoxModel to show in a TableListBox.
  583. */
  584. class TableDemoComponent : public Component,
  585. public TableListBoxModel
  586. {
  587. public:
  588. TableDemoComponent() : font (14.0f)
  589. {
  590. // Load some data from an embedded XML file..
  591. loadData();
  592. // Create our table component and add it to this component..
  593. addAndMakeVisible (&table);
  594. table.setModel (this);
  595. // give it a border
  596. table.setColour (ListBox::outlineColourId, Colours::grey);
  597. table.setOutlineThickness (1);
  598. // Add some columns to the table header, based on the column list in our database..
  599. forEachXmlChildElement (*columnList, columnXml)
  600. {
  601. table.getHeader().addColumn (columnXml->getStringAttribute ("name"),
  602. columnXml->getIntAttribute ("columnId"),
  603. columnXml->getIntAttribute ("width"),
  604. 50, 400,
  605. TableHeaderComponent::defaultFlags);
  606. }
  607. // we could now change some initial settings..
  608. table.getHeader().setSortColumnId (1, true); // sort forwards by the ID column
  609. table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
  610. // un-comment this line to have a go of stretch-to-fit mode
  611. // table.getHeader().setStretchToFitActive (true);
  612. table.setMultipleSelectionEnabled (true);
  613. }
  614. // This is overloaded from TableListBoxModel, and must return the total number of rows in our table
  615. int getNumRows() override
  616. {
  617. return numRows;
  618. }
  619. // This is overloaded from TableListBoxModel, and should fill in the background of the whole row
  620. void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override
  621. {
  622. if (rowIsSelected)
  623. g.fillAll (Colours::lightblue);
  624. }
  625. // This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
  626. // components.
  627. void paintCell (Graphics& g,
  628. int rowNumber,
  629. int columnId,
  630. int width, int height,
  631. bool /*rowIsSelected*/) override
  632. {
  633. g.setColour (Colours::black);
  634. g.setFont (font);
  635. const XmlElement* rowElement = dataList->getChildElement (rowNumber);
  636. if (rowElement != 0)
  637. {
  638. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  639. g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
  640. }
  641. g.setColour (Colours::black.withAlpha (0.2f));
  642. g.fillRect (width - 1, 0, 1, height);
  643. }
  644. // This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
  645. // to change the sort order.
  646. void sortOrderChanged (int newSortColumnId, bool isForwards) override
  647. {
  648. if (newSortColumnId != 0)
  649. {
  650. DemoDataSorter sorter (getAttributeNameForColumnId (newSortColumnId), isForwards);
  651. dataList->sortChildElements (sorter);
  652. table.updateContent();
  653. }
  654. }
  655. // This is overloaded from TableListBoxModel, and must update any custom components that we're using
  656. Component* refreshComponentForCell (int rowNumber, int columnId, bool /*isRowSelected*/,
  657. Component* existingComponentToUpdate) override
  658. {
  659. if (columnId == 5) // If it's the ratings column, we'll return our custom component..
  660. {
  661. RatingColumnCustomComponent* ratingsBox = (RatingColumnCustomComponent*) existingComponentToUpdate;
  662. // If an existing component is being passed-in for updating, we'll re-use it, but
  663. // if not, we'll have to create one.
  664. if (ratingsBox == 0)
  665. ratingsBox = new RatingColumnCustomComponent (*this);
  666. ratingsBox->setRowAndColumn (rowNumber, columnId);
  667. return ratingsBox;
  668. }
  669. else
  670. {
  671. // for any other column, just return 0, as we'll be painting these columns directly.
  672. jassert (existingComponentToUpdate == 0);
  673. return 0;
  674. }
  675. }
  676. // This is overloaded from TableListBoxModel, and should choose the best width for the specified
  677. // column.
  678. int getColumnAutoSizeWidth (int columnId) override
  679. {
  680. if (columnId == 5)
  681. return 100; // (this is the ratings column, containing a custom component)
  682. int widest = 32;
  683. // find the widest bit of text in this column..
  684. for (int i = getNumRows(); --i >= 0;)
  685. {
  686. const XmlElement* rowElement = dataList->getChildElement (i);
  687. if (rowElement != 0)
  688. {
  689. const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
  690. widest = jmax (widest, font.getStringWidth (text));
  691. }
  692. }
  693. return widest + 8;
  694. }
  695. // A couple of quick methods to set and get the "rating" value when the user
  696. // changes the combo box
  697. int getRating (const int rowNumber) const
  698. {
  699. return dataList->getChildElement (rowNumber)->getIntAttribute ("Rating");
  700. }
  701. void setRating (const int rowNumber, const int newRating)
  702. {
  703. dataList->getChildElement (rowNumber)->setAttribute ("Rating", newRating);
  704. }
  705. //==============================================================================
  706. void resized() override
  707. {
  708. // position our table with a gap around its edge
  709. table.setBoundsInset (BorderSize<int> (8));
  710. }
  711. private:
  712. TableListBox table; // the table component itself
  713. Font font;
  714. ScopedPointer<XmlElement> demoData; // This is the XML document loaded from the embedded file "demo table data.xml"
  715. XmlElement* columnList; // A pointer to the sub-node of demoData that contains the list of columns
  716. XmlElement* dataList; // A pointer to the sub-node of demoData that contains the list of data rows
  717. int numRows; // The number of rows of data we've got
  718. //==============================================================================
  719. // This is a custom component containing a combo box, which we're going to put inside
  720. // our table's "rating" column.
  721. class RatingColumnCustomComponent : public Component,
  722. public ComboBoxListener
  723. {
  724. public:
  725. RatingColumnCustomComponent (TableDemoComponent& owner_)
  726. : owner (owner_)
  727. {
  728. // just put a combo box inside this component
  729. addAndMakeVisible (&comboBox);
  730. comboBox.addItem ("fab", 1);
  731. comboBox.addItem ("groovy", 2);
  732. comboBox.addItem ("hep", 3);
  733. comboBox.addItem ("neat", 4);
  734. comboBox.addItem ("wild", 5);
  735. comboBox.addItem ("swingin", 6);
  736. comboBox.addItem ("mad for it", 7);
  737. // when the combo is changed, we'll get a callback.
  738. comboBox.addListener (this);
  739. comboBox.setWantsKeyboardFocus (false);
  740. }
  741. void resized() override
  742. {
  743. comboBox.setBoundsInset (BorderSize<int> (2));
  744. }
  745. // Our demo code will call this when we may need to update our contents
  746. void setRowAndColumn (const int newRow, const int newColumn)
  747. {
  748. row = newRow;
  749. columnId = newColumn;
  750. comboBox.setSelectedId (owner.getRating (row), dontSendNotification);
  751. }
  752. void comboBoxChanged (ComboBox* /*comboBoxThatHasChanged*/) override
  753. {
  754. owner.setRating (row, comboBox.getSelectedId());
  755. }
  756. private:
  757. TableDemoComponent& owner;
  758. ComboBox comboBox;
  759. int row, columnId;
  760. };
  761. //==============================================================================
  762. // A comparator used to sort our data when the user clicks a column header
  763. class DemoDataSorter
  764. {
  765. public:
  766. DemoDataSorter (const String attributeToSort_, bool forwards)
  767. : attributeToSort (attributeToSort_),
  768. direction (forwards ? 1 : -1)
  769. {
  770. }
  771. int compareElements (XmlElement* first, XmlElement* second) const
  772. {
  773. int result = first->getStringAttribute (attributeToSort)
  774. .compareLexicographically (second->getStringAttribute (attributeToSort));
  775. if (result == 0)
  776. result = first->getStringAttribute ("ID")
  777. .compareLexicographically (second->getStringAttribute ("ID"));
  778. return direction * result;
  779. }
  780. private:
  781. String attributeToSort;
  782. int direction;
  783. };
  784. //==============================================================================
  785. // this loads the embedded database XML file into memory
  786. void loadData()
  787. {
  788. XmlDocument dataDoc (String ((const char*) BinaryData::demo_table_data_xml));
  789. demoData = dataDoc.getDocumentElement();
  790. dataList = demoData->getChildByName ("DATA");
  791. columnList = demoData->getChildByName ("COLUMNS");
  792. numRows = dataList->getNumChildElements();
  793. }
  794. // (a utility method to search our XML for the attribute that matches a column ID)
  795. const String getAttributeNameForColumnId (const int columnId) const
  796. {
  797. forEachXmlChildElement (*columnList, columnXml)
  798. {
  799. if (columnXml->getIntAttribute ("columnId") == columnId)
  800. return columnXml->getStringAttribute ("name");
  801. }
  802. return String::empty;
  803. }
  804. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableDemoComponent)
  805. };
  806. //==============================================================================
  807. class DragAndDropDemo : public Component,
  808. public DragAndDropContainer
  809. {
  810. public:
  811. DragAndDropDemo()
  812. : sourceListBox ("D+D source", nullptr)
  813. {
  814. setName ("Drag-and-Drop");
  815. sourceListBox.setModel (&sourceModel);
  816. sourceListBox.setMultipleSelectionEnabled (true);
  817. addAndMakeVisible (&sourceListBox);
  818. addAndMakeVisible (&target);
  819. }
  820. void resized() override
  821. {
  822. Rectangle<int> r (getLocalBounds().reduced (8));
  823. sourceListBox.setBounds (r.withSize (250, 180));
  824. target.setBounds (r.removeFromBottom (150).removeFromRight (250));
  825. }
  826. private:
  827. //==============================================================================
  828. struct SourceItemListboxContents : public ListBoxModel
  829. {
  830. // The following methods implement the necessary virtual functions from ListBoxModel,
  831. // telling the listbox how many rows there are, painting them, etc.
  832. int getNumRows()
  833. {
  834. return 30;
  835. }
  836. void paintListBoxItem (int rowNumber,
  837. Graphics& g,
  838. int width, int height,
  839. bool rowIsSelected)
  840. {
  841. if (rowIsSelected)
  842. g.fillAll (Colours::lightblue);
  843. g.setColour (Colours::black);
  844. g.setFont (height * 0.7f);
  845. g.drawText ("Draggable Thing #" + String (rowNumber + 1),
  846. 5, 0, width, height,
  847. Justification::centredLeft, true);
  848. }
  849. var getDragSourceDescription (const SparseSet<int>& selectedRows)
  850. {
  851. // for our drag description, we'll just make a comma-separated list of the selected row
  852. // numbers - this will be picked up by the drag target and displayed in its box.
  853. StringArray rows;
  854. for (int i = 0; i < selectedRows.size(); ++i)
  855. rows.add (String (selectedRows[i] + 1));
  856. return rows.joinIntoString (", ");
  857. }
  858. // this just fills in the background of the listbox
  859. void paint (Graphics& g)
  860. {
  861. g.fillAll (Colours::white.withAlpha (0.7f));
  862. }
  863. };
  864. //==============================================================================
  865. // and this is a component that can have things dropped onto it..
  866. class DragAndDropDemoTarget : public Component,
  867. public DragAndDropTarget,
  868. public FileDragAndDropTarget,
  869. public TextDragAndDropTarget
  870. {
  871. public:
  872. DragAndDropDemoTarget()
  873. : message ("Drag-and-drop some rows from the top-left box onto this component!\n\n"
  874. "You can also drag-and-drop files and text from other apps"),
  875. somethingIsBeingDraggedOver (false)
  876. {
  877. }
  878. void paint (Graphics& g) override
  879. {
  880. g.fillAll (Colours::green.withAlpha (0.2f));
  881. // draw a red line around the comp if the user's currently dragging something over it..
  882. if (somethingIsBeingDraggedOver)
  883. {
  884. g.setColour (Colours::red);
  885. g.drawRect (getLocalBounds(), 3);
  886. }
  887. g.setColour (Colours::black);
  888. g.setFont (14.0f);
  889. g.drawFittedText (message, getLocalBounds().reduced (10, 0), Justification::centred, 4);
  890. }
  891. //==============================================================================
  892. // These methods implement the DragAndDropTarget interface, and allow our component
  893. // to accept drag-and-drop of objects from other Juce components..
  894. bool isInterestedInDragSource (const SourceDetails& /*dragSourceDetails*/) override
  895. {
  896. // normally you'd check the sourceDescription value to see if it's the
  897. // sort of object that you're interested in before returning true, but for
  898. // the demo, we'll say yes to anything..
  899. return true;
  900. }
  901. void itemDragEnter (const SourceDetails& /*dragSourceDetails*/) override
  902. {
  903. somethingIsBeingDraggedOver = true;
  904. repaint();
  905. }
  906. void itemDragMove (const SourceDetails& /*dragSourceDetails*/) override
  907. {
  908. }
  909. void itemDragExit (const SourceDetails& /*dragSourceDetails*/) override
  910. {
  911. somethingIsBeingDraggedOver = false;
  912. repaint();
  913. }
  914. void itemDropped (const SourceDetails& dragSourceDetails) override
  915. {
  916. message = "Items dropped: " + dragSourceDetails.description.toString();
  917. somethingIsBeingDraggedOver = false;
  918. repaint();
  919. }
  920. //==============================================================================
  921. // These methods implement the FileDragAndDropTarget interface, and allow our component
  922. // to accept drag-and-drop of files..
  923. bool isInterestedInFileDrag (const StringArray& /*files*/) override
  924. {
  925. // normally you'd check these files to see if they're something that you're
  926. // interested in before returning true, but for the demo, we'll say yes to anything..
  927. return true;
  928. }
  929. void fileDragEnter (const StringArray& /*files*/, int /*x*/, int /*y*/) override
  930. {
  931. somethingIsBeingDraggedOver = true;
  932. repaint();
  933. }
  934. void fileDragMove (const StringArray& /*files*/, int /*x*/, int /*y*/) override
  935. {
  936. }
  937. void fileDragExit (const StringArray& /*files*/) override
  938. {
  939. somethingIsBeingDraggedOver = false;
  940. repaint();
  941. }
  942. void filesDropped (const StringArray& files, int /*x*/, int /*y*/) override
  943. {
  944. message = "Files dropped: " + files.joinIntoString ("\n");
  945. somethingIsBeingDraggedOver = false;
  946. repaint();
  947. }
  948. //==============================================================================
  949. // These methods implement the TextDragAndDropTarget interface, and allow our component
  950. // to accept drag-and-drop of text..
  951. bool isInterestedInTextDrag (const String& /*text*/) override
  952. {
  953. return true;
  954. }
  955. void textDragEnter (const String& /*text*/, int /*x*/, int /*y*/) override
  956. {
  957. somethingIsBeingDraggedOver = true;
  958. repaint();
  959. }
  960. void textDragMove (const String& /*text*/, int /*x*/, int /*y*/) override
  961. {
  962. }
  963. void textDragExit (const String& /*text*/) override
  964. {
  965. somethingIsBeingDraggedOver = false;
  966. repaint();
  967. }
  968. void textDropped (const String& text, int /*x*/, int /*y*/) override
  969. {
  970. message = "Text dropped:\n" + text;
  971. somethingIsBeingDraggedOver = false;
  972. repaint();
  973. }
  974. private:
  975. String message;
  976. bool somethingIsBeingDraggedOver;
  977. };
  978. //==============================================================================
  979. ListBox sourceListBox;
  980. SourceItemListboxContents sourceModel;
  981. DragAndDropDemoTarget target;
  982. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropDemo)
  983. };
  984. //==============================================================================
  985. class MenusDemo : public Component,
  986. public MenuBarModel,
  987. private Button::Listener
  988. {
  989. public:
  990. MenusDemo()
  991. {
  992. addAndMakeVisible (menuBar = new MenuBarComponent (this));
  993. popupButton.setButtonText ("Show Popup Menu");
  994. popupButton.setTriggeredOnMouseDown (true);
  995. popupButton.addListener (this);
  996. addAndMakeVisible (&popupButton);
  997. }
  998. ~MenusDemo()
  999. {
  1000. #if JUCE_MAC
  1001. MenuBarModel::setMacMainMenu (nullptr);
  1002. #endif
  1003. PopupMenu::dismissAllActiveMenus();
  1004. popupButton.removeListener (this);
  1005. }
  1006. void resized() override
  1007. {
  1008. Rectangle<int> area (getLocalBounds());
  1009. menuBar->setBounds (area.removeFromTop (LookAndFeel::getDefaultLookAndFeel().getDefaultMenuBarHeight()));
  1010. area.removeFromTop (20);
  1011. area = area.removeFromTop (33);
  1012. popupButton.setBounds (area.removeFromLeft (200).reduced (5));
  1013. }
  1014. //==============================================================================
  1015. StringArray getMenuBarNames() override
  1016. {
  1017. const char* const names[] = { "Demo", "Look-and-feel", "Tabs", "Misc", nullptr };
  1018. return StringArray (names);
  1019. }
  1020. PopupMenu getMenuForIndex (int menuIndex, const String& /*menuName*/) override
  1021. {
  1022. ApplicationCommandManager* commandManager = &MainAppWindow::getApplicationCommandManager();
  1023. PopupMenu menu;
  1024. if (menuIndex == 0)
  1025. {
  1026. menu.addCommandItem (commandManager, MainAppWindow::showPreviousDemo);
  1027. menu.addCommandItem (commandManager, MainAppWindow::showNextDemo);
  1028. menu.addSeparator();
  1029. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  1030. }
  1031. else if (menuIndex == 1)
  1032. {
  1033. menu.addCommandItem (commandManager, MainAppWindow::useLookAndFeelV1);
  1034. menu.addCommandItem (commandManager, MainAppWindow::useLookAndFeelV2);
  1035. menu.addCommandItem (commandManager, MainAppWindow::useLookAndFeelV3);
  1036. menu.addSeparator();
  1037. menu.addCommandItem (commandManager, MainAppWindow::useNativeTitleBar);
  1038. #if JUCE_MAC
  1039. menu.addItem (6000, "Use Native Menu Bar");
  1040. #endif
  1041. #if ! JUCE_LINUX
  1042. menu.addCommandItem (commandManager, MainAppWindow::goToKioskMode);
  1043. #endif
  1044. if (MainAppWindow* mainWindow = MainAppWindow::getMainAppWindow())
  1045. {
  1046. StringArray engines (mainWindow->getRenderingEngines());
  1047. if (engines.size() > 1)
  1048. {
  1049. menu.addSeparator();
  1050. for (int i = 0; i < engines.size(); ++i)
  1051. menu.addCommandItem (commandManager, MainAppWindow::renderingEngineOne + i);
  1052. }
  1053. }
  1054. }
  1055. else if (menuIndex == 2)
  1056. {
  1057. if (TabbedComponent* tabs = findParentComponentOfClass<TabbedComponent>())
  1058. {
  1059. menu.addItem (3000, "Tabs at Top", true, tabs->getOrientation() == TabbedButtonBar::TabsAtTop);
  1060. menu.addItem (3001, "Tabs at Bottom", true, tabs->getOrientation() == TabbedButtonBar::TabsAtBottom);
  1061. menu.addItem (3002, "Tabs on Left", true, tabs->getOrientation() == TabbedButtonBar::TabsAtLeft);
  1062. menu.addItem (3003, "Tabs on Right", true, tabs->getOrientation() == TabbedButtonBar::TabsAtRight);
  1063. }
  1064. }
  1065. else if (menuIndex == 3)
  1066. {
  1067. return getDummyPopupMenu();
  1068. }
  1069. return menu;
  1070. }
  1071. void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
  1072. {
  1073. // most of our menu items are invoked automatically as commands, but we can handle the
  1074. // other special cases here..
  1075. if (menuItemID == 6000)
  1076. {
  1077. #if JUCE_MAC
  1078. if (MenuBarModel::getMacMainMenu() != nullptr)
  1079. {
  1080. MenuBarModel::setMacMainMenu (nullptr);
  1081. menuBar->setModel (this);
  1082. }
  1083. else
  1084. {
  1085. menuBar->setModel (nullptr);
  1086. MenuBarModel::setMacMainMenu (this);
  1087. }
  1088. #endif
  1089. }
  1090. else if (menuItemID >= 3000 && menuItemID <= 3003)
  1091. {
  1092. if (TabbedComponent* tabs = findParentComponentOfClass<TabbedComponent>())
  1093. {
  1094. TabbedButtonBar::Orientation o = TabbedButtonBar::TabsAtTop;
  1095. if (menuItemID == 3001) o = TabbedButtonBar::TabsAtBottom;
  1096. if (menuItemID == 3002) o = TabbedButtonBar::TabsAtLeft;
  1097. if (menuItemID == 3003) o = TabbedButtonBar::TabsAtRight;
  1098. tabs->setOrientation (o);
  1099. }
  1100. }
  1101. }
  1102. private:
  1103. TextButton popupButton;
  1104. ScopedPointer<MenuBarComponent> menuBar;
  1105. PopupMenu getDummyPopupMenu()
  1106. {
  1107. PopupMenu m;
  1108. m.addItem (1, "Normal item");
  1109. m.addItem (2, "Disabled item", false);
  1110. m.addItem (3, "Ticked item", true, true);
  1111. m.addColouredItem (4, "Coloured item", Colours::green);
  1112. m.addSeparator();
  1113. m.addCustomItem (5, new CustomMenuComponent());
  1114. m.addSeparator();
  1115. for (int i = 0; i < 8; ++i)
  1116. {
  1117. PopupMenu subMenu;
  1118. for (int s = 0; s < 8; ++s)
  1119. {
  1120. PopupMenu subSubMenu;
  1121. for (int item = 0; item < 8; ++item)
  1122. subSubMenu.addItem (1000 + (i * s * item), "Item " + String (item + 1));
  1123. subMenu.addSubMenu ("Sub-sub menu " + String (s + 1), subSubMenu);
  1124. }
  1125. m.addSubMenu ("Sub menu " + String (i + 1), subMenu);
  1126. }
  1127. return m;
  1128. }
  1129. //==============================================================================
  1130. void buttonClicked (Button* button) override
  1131. {
  1132. if (button == &popupButton)
  1133. getDummyPopupMenu().showMenuAsync (PopupMenu::Options().withTargetComponent (&popupButton), nullptr);
  1134. }
  1135. //==============================================================================
  1136. class CustomMenuComponent : public PopupMenu::CustomComponent,
  1137. private Timer
  1138. {
  1139. public:
  1140. CustomMenuComponent()
  1141. {
  1142. // set off a timer to move a blob around on this component every
  1143. // 300 milliseconds - see the timerCallback() method.
  1144. startTimer (300);
  1145. }
  1146. void getIdealSize (int& idealWidth, int& idealHeight) override
  1147. {
  1148. // tells the menu how big we'd like to be..
  1149. idealWidth = 200;
  1150. idealHeight = 60;
  1151. }
  1152. void paint (Graphics& g) override
  1153. {
  1154. g.fillAll (Colours::yellow.withAlpha (0.3f));
  1155. g.setColour (Colours::pink);
  1156. g.fillEllipse (blobPosition);
  1157. g.setFont (Font (14.0f, Font::italic));
  1158. g.setColour (Colours::black);
  1159. g.drawFittedText ("This is a customised menu item (also demonstrating the Timer class)...",
  1160. getLocalBounds().reduced (4, 0),
  1161. Justification::centred, 3);
  1162. }
  1163. private:
  1164. void timerCallback() override
  1165. {
  1166. Random random;
  1167. blobPosition.setBounds ((float) random.nextInt (getWidth()),
  1168. (float) random.nextInt (getHeight()),
  1169. 40.0f, 30.0f);
  1170. repaint();
  1171. }
  1172. Rectangle<float> blobPosition;
  1173. };
  1174. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenusDemo)
  1175. };
  1176. //==============================================================================
  1177. class DemoTabbedComponent : public TabbedComponent
  1178. {
  1179. public:
  1180. DemoTabbedComponent()
  1181. : TabbedComponent (TabbedButtonBar::TabsAtTop)
  1182. {
  1183. addTab ("Menus", getRandomTabBackgroundColour(), new MenusDemo(), true);
  1184. addTab ("Buttons", getRandomTabBackgroundColour(), new ButtonsPage(), true);
  1185. addTab ("Sliders", getRandomTabBackgroundColour(), new SlidersPage(), true);
  1186. addTab ("Toolbars", getRandomTabBackgroundColour(), new ToolbarDemoComp(), true);
  1187. addTab ("Misc", getRandomTabBackgroundColour(), new MiscPage(), true);
  1188. addTab ("Tables", getRandomTabBackgroundColour(), new TableDemoComponent(), true);
  1189. addTab ("Drag & Drop", getRandomTabBackgroundColour(), new DragAndDropDemo(), true);
  1190. getTabbedButtonBar().getTabButton (5)->setExtraComponent (new CustomTabButton(), TabBarButton::afterText);
  1191. }
  1192. static Colour getRandomTabBackgroundColour()
  1193. {
  1194. return Colour (Random::getSystemRandom().nextFloat(), 0.1f, 0.97f, 1.0f);
  1195. }
  1196. // This is a small star button that is put inside one of the tabs. You can
  1197. // use this technique to create things like "close tab" buttons, etc.
  1198. class CustomTabButton : public Component
  1199. {
  1200. public:
  1201. CustomTabButton()
  1202. {
  1203. setSize (20, 20);
  1204. }
  1205. void paint (Graphics& g)
  1206. {
  1207. Path star;
  1208. star.addStar (Point<float>(), 7, 1.0f, 2.0f);
  1209. g.setColour (Colours::green);
  1210. g.fillPath (star, star.getTransformToScaleToFit (getLocalBounds().reduced (2).toFloat(), true));
  1211. }
  1212. void mouseDown (const MouseEvent&)
  1213. {
  1214. showBubbleMessage (this,
  1215. "This is a custom tab component\n"
  1216. "\n"
  1217. "You can use these to implement things like close-buttons "
  1218. "or status displays for your tabs.");
  1219. }
  1220. };
  1221. };
  1222. //==============================================================================
  1223. class WidgetsDemo : public Component
  1224. {
  1225. public:
  1226. WidgetsDemo()
  1227. {
  1228. setOpaque (true);
  1229. addAndMakeVisible (&tabs);
  1230. }
  1231. void paint (Graphics& g)
  1232. {
  1233. g.fillAll (Colours::white);
  1234. }
  1235. void resized()
  1236. {
  1237. tabs.setBounds (getLocalBounds().reduced (4));
  1238. }
  1239. private:
  1240. DemoTabbedComponent tabs;
  1241. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WidgetsDemo);
  1242. };
  1243. // This static object will register this demo type in a global list of demos..
  1244. static JuceDemoType<WidgetsDemo> demo ("09 Components: Tabs & Widgets");