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.

284 lines
9.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include "../../Project/UI/jucer_UserAvatarComponent.h"
  20. //==============================================================================
  21. class LoginFormComponent : public Component
  22. {
  23. public:
  24. LoginFormComponent (MainWindow& window)
  25. : mainWindow (window)
  26. {
  27. setTitle ("Login");
  28. setFocusContainerType (FocusContainerType::focusContainer);
  29. addAndMakeVisible (emailBox);
  30. emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f));
  31. emailBox.setJustification (Justification::centredLeft);
  32. emailBox.onReturnKey = [this] { submitDetails(); };
  33. emailBox.setTitle ("Email");
  34. addAndMakeVisible (passwordBox);
  35. passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f));
  36. passwordBox.setPasswordCharacter ((juce_wchar) 0x2022);
  37. passwordBox.setJustification (Justification::centredLeft);
  38. passwordBox.onReturnKey = [this] { submitDetails(); };
  39. passwordBox.setTitle ("Password");
  40. addAndMakeVisible (logInButton);
  41. logInButton.onClick = [this] { submitDetails(); };
  42. addAndMakeVisible (enableGPLButton);
  43. enableGPLButton.onClick = [this]
  44. {
  45. ProjucerApplication::getApp().getLicenseController().setState (LicenseController::getGPLState());
  46. mainWindow.hideLoginFormOverlay();
  47. };
  48. addAndMakeVisible (userAvatar);
  49. addAndMakeVisible (createAccountLabel);
  50. createAccountLabel.setFont (Font (14.0f, Font::underlined));
  51. createAccountLabel.addMouseListener (this, false);
  52. createAccountLabel.setMouseCursor (MouseCursor::PointingHandCursor);
  53. addAndMakeVisible (errorMessageLabel);
  54. errorMessageLabel.setMinimumHorizontalScale (1.0f);
  55. errorMessageLabel.setFont (12.0f);
  56. errorMessageLabel.setColour (Label::textColourId, Colours::red);
  57. errorMessageLabel.setVisible (false);
  58. dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
  59. addAndMakeVisible (dismissButton);
  60. dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); };
  61. dismissButton.setTitle ("Dismiss");
  62. setWantsKeyboardFocus (true);
  63. setOpaque (true);
  64. lookAndFeelChanged();
  65. setSize (300, 350);
  66. }
  67. ~LoginFormComponent() override
  68. {
  69. ProjucerApplication::getApp().getLicenseController().cancelSignIn();
  70. }
  71. void resized() override
  72. {
  73. auto bounds = getLocalBounds().reduced (20);
  74. auto spacing = bounds.getHeight() / 20;
  75. userAvatar.setBounds (bounds.removeFromTop (iconHeight).reduced ((bounds.getWidth() / 2) - (iconHeight / 2), 0));
  76. errorMessageLabel.setBounds (bounds.removeFromTop (spacing));
  77. bounds.removeFromTop (spacing / 2);
  78. auto textEditorHeight = bounds.getHeight() / 5;
  79. emailBox.setBounds (bounds.removeFromTop (textEditorHeight));
  80. bounds.removeFromTop (spacing);
  81. passwordBox.setBounds (bounds.removeFromTop (textEditorHeight));
  82. bounds.removeFromTop (spacing * 2);
  83. emailBox.setFont (Font ((float) textEditorHeight / 2.5f));
  84. passwordBox.setFont (Font ((float) textEditorHeight / 2.5f));
  85. logInButton.setBounds (bounds.removeFromTop (textEditorHeight));
  86. auto slice = bounds.removeFromTop (textEditorHeight);
  87. createAccountLabel.setBounds (slice.removeFromLeft (createAccountLabel.getFont().getStringWidth (createAccountLabel.getText()) + 5));
  88. slice.removeFromLeft (15);
  89. enableGPLButton.setBounds (slice.reduced (0, 5));
  90. dismissButton.setBounds (getLocalBounds().reduced (10).removeFromTop (20).removeFromRight (20));
  91. }
  92. void paint (Graphics& g) override
  93. {
  94. g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
  95. }
  96. void mouseUp (const MouseEvent& event) override
  97. {
  98. if (event.eventComponent == &createAccountLabel)
  99. URL ("https://juce.com/verification/register").launchInDefaultBrowser();
  100. }
  101. void lookAndFeelChanged() override
  102. {
  103. enableGPLButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  104. }
  105. private:
  106. class ProgressButton : public TextButton,
  107. private Timer
  108. {
  109. public:
  110. ProgressButton (const String& buttonName)
  111. : TextButton (buttonName), text (buttonName)
  112. {
  113. }
  114. void setBusy (bool shouldBeBusy)
  115. {
  116. isInProgress = shouldBeBusy;
  117. if (isInProgress)
  118. {
  119. setEnabled (false);
  120. setButtonText ({});
  121. startTimerHz (30);
  122. }
  123. else
  124. {
  125. setEnabled (true);
  126. setButtonText (text);
  127. stopTimer();
  128. }
  129. }
  130. void paint (Graphics& g) override
  131. {
  132. TextButton::paint (g);
  133. if (isInProgress)
  134. {
  135. auto size = getHeight() - 10;
  136. auto halfSize = size / 2;
  137. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white,
  138. (getWidth() / 2) - halfSize, (getHeight() / 2) - halfSize,
  139. size, size);
  140. }
  141. }
  142. private:
  143. void timerCallback() override
  144. {
  145. repaint();
  146. }
  147. String text;
  148. bool isInProgress = false;
  149. };
  150. //==============================================================================
  151. void updateLoginButtonStates (bool isLoggingIn)
  152. {
  153. logInButton.setBusy (isLoggingIn);
  154. emailBox.setEnabled (! isLoggingIn);
  155. passwordBox.setEnabled (! isLoggingIn);
  156. }
  157. void submitDetails()
  158. {
  159. auto loginFormError = checkLoginFormsAreValid();
  160. if (loginFormError.isNotEmpty())
  161. {
  162. showErrorMessage (loginFormError);
  163. return;
  164. }
  165. updateLoginButtonStates (true);
  166. auto completionCallback = [weakThis = SafePointer<LoginFormComponent> { this }] (const String& errorMessage)
  167. {
  168. if (weakThis == nullptr)
  169. return;
  170. weakThis->updateLoginButtonStates (false);
  171. if (errorMessage.isNotEmpty())
  172. {
  173. weakThis->showErrorMessage (errorMessage);
  174. }
  175. else
  176. {
  177. weakThis->hideErrorMessage();
  178. weakThis->mainWindow.hideLoginFormOverlay();
  179. ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
  180. }
  181. };
  182. ProjucerApplication::getApp().getLicenseController().signIn (emailBox.getText(), passwordBox.getText(),
  183. std::move (completionCallback));
  184. }
  185. String checkLoginFormsAreValid() const
  186. {
  187. auto email = emailBox.getText();
  188. if (email.isEmpty() || email.indexOfChar ('@') < 0)
  189. return "Please enter a valid email.";
  190. auto password = passwordBox.getText();
  191. if (password.isEmpty() || password.length() < 8)
  192. return "Please enter a valid password.";
  193. return {};
  194. }
  195. void showErrorMessage (const String& errorMessage)
  196. {
  197. errorMessageLabel.setText (errorMessage, dontSendNotification);
  198. errorMessageLabel.setVisible (true);
  199. }
  200. void hideErrorMessage()
  201. {
  202. errorMessageLabel.setText ({}, dontSendNotification);
  203. errorMessageLabel.setVisible (false);
  204. }
  205. //==============================================================================
  206. static constexpr int iconHeight = 50;
  207. MainWindow& mainWindow;
  208. TextEditor emailBox, passwordBox;
  209. ProgressButton logInButton { "Sign In" };
  210. TextButton enableGPLButton { "Enable GPL Mode" };
  211. ShapeButton dismissButton { {},
  212. findColour (treeIconColourId),
  213. findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
  214. findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
  215. UserAvatarComponent userAvatar { false };
  216. Label createAccountLabel { {}, "Create an account" },
  217. errorMessageLabel { {}, {} };
  218. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent)
  219. };