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

GUI Basics: Refactor juce_gui_basics file structure

- Created a new detail namespace
- Moved shared module implementation details into the detail namespace
- Split dependencies so source files only rely on details in the detail namespace
- Removed all code from the juce_gui_basics.cpp file
This commit is contained in:
Anthony Nicholls 2023-03-03 10:17:48 +00:00
parent 8942f22a9b
commit cff722a4af
129 changed files with 4458 additions and 2318 deletions

View file

@ -35,90 +35,9 @@ static juce_wchar getDefaultPasswordChar() noexcept
#endif
}
static std::unique_ptr<ScopedMessageBoxInterface> createAlertWindowImpl (const MessageBoxOptions& opts)
{
class AlertWindowImpl : public ScopedMessageBoxInterface
{
public:
explicit AlertWindowImpl (const MessageBoxOptions& opts) : options (opts) {}
void runAsync (std::function<void (int)> recipient) override
{
if (auto* comp = setUpAlert())
comp->enterModalState (true, ModalCallbackFunction::create (std::move (recipient)), true);
else
NullCheckedInvocation::invoke (recipient, 0);
}
int runSync() override
{
#if JUCE_MODAL_LOOPS_PERMITTED
if (auto comp = rawToUniquePtr (setUpAlert()))
return comp->runModalLoop();
#endif
jassertfalse;
return 0;
}
void close() override
{
if (alert != nullptr)
if (alert->isCurrentlyModal())
alert->exitModalState();
alert = nullptr;
}
private:
Component* setUpAlert()
{
auto* component = options.getAssociatedComponent();
auto& lf = component != nullptr ? component->getLookAndFeel()
: LookAndFeel::getDefaultLookAndFeel();
alert = lf.createAlertWindow (options.getTitle(),
options.getMessage(),
options.getButtonText (0),
options.getButtonText (1),
options.getButtonText (2),
options.getIconType(),
options.getNumButtons(),
component);
if (alert == nullptr)
{
// You have to return an alert box!
jassertfalse;
return nullptr;
}
if (auto* parent = options.getParentComponent())
{
parent->addAndMakeVisible (alert);
if (options.getAssociatedComponent() == nullptr)
alert->setCentrePosition (parent->getLocalBounds().getCentre());
}
alert->setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
return alert;
}
const MessageBoxOptions options;
Component::SafePointer<AlertWindow> alert;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindowImpl)
};
return std::make_unique<AlertWindowImpl> (opts);
}
static int showAlertWindowUnmanaged (const MessageBoxOptions& opts, ModalComponentManager::Callback* cb)
{
return ScopedMessageBox::Pimpl::showUnmanaged (createAlertWindowImpl (opts), cb);
return detail::ScopedMessageBoxImpl::showUnmanaged (detail::AlertWindowHelpers::create (opts), cb);
}
//==============================================================================
@ -131,7 +50,7 @@ AlertWindow::AlertWindow (const String& title,
associatedComponent (comp),
desktopScale (comp != nullptr ? Component::getApproximateScaleFactorForComponent (comp) : 1.0f)
{
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
setAlwaysOnTop (detail::WindowingHelpers::areThereAnyAlwaysOnTopWindows());
accessibleMessageLabel.setColour (Label::textColourId, Colours::transparentBlack);
accessibleMessageLabel.setColour (Label::backgroundColourId, Colours::transparentBlack);
@ -774,7 +693,7 @@ ScopedMessageBox AlertWindow::showScopedAsync (const MessageBoxOptions& options,
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
return NativeMessageBox::showScopedAsync (options, std::move (callback));
return ScopedMessageBox::Pimpl::show (createAlertWindowImpl (options), std::move (callback));
return detail::ScopedMessageBoxImpl::show (detail::AlertWindowHelpers::create (options), std::move (callback));
}
//==============================================================================

View file

@ -39,7 +39,7 @@ CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const pare
}
else
{
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
setAlwaysOnTop (detail::WindowingHelpers::areThereAnyAlwaysOnTopWindows());
updatePosition (area, Desktop::getInstance().getDisplays().getDisplayForRect (area)->userArea);
addToDesktop (ComponentPeer::windowIsTemporary);
@ -67,7 +67,7 @@ public:
void timerCallback() override
{
if (! isForegroundOrEmbeddedProcess (&callout))
if (! detail::WindowingHelpers::isForegroundOrEmbeddedProcess (&callout))
callout.dismiss();
}

View file

@ -74,7 +74,7 @@ bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
void ComponentPeer::updateBounds()
{
setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
setBounds (detail::ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
}
bool ComponentPeer::isKioskMode() const
@ -315,7 +315,7 @@ void ComponentPeer::handleMovedOrResized()
{
const WeakReference<Component> deletionChecker (&component);
auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
auto newBounds = detail::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
auto oldBounds = component.getBounds();
const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition());
@ -430,7 +430,7 @@ Rectangle<float> ComponentPeer::globalToLocal (const Rectangle<float>& screenPos
Rectangle<int> ComponentPeer::getAreaCoveredBy (const Component& subComponent) const
{
return ScalingHelpers::scaledScreenPosToUnscaled
return detail::ScalingHelpers::scaledScreenPosToUnscaled
(component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
}

View file

@ -92,7 +92,7 @@ public:
setResizable (options.resizable, options.useBottomRightCornerResizer);
setUsingNativeTitleBar (options.useNativeTitleBar);
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
setAlwaysOnTop (detail::WindowingHelpers::areThereAnyAlwaysOnTopWindows());
}
void closeButtonPressed() override

View file

@ -34,14 +34,14 @@ enum class ResultCodeMappingMode
// are N buttons then button X will return ((X + 1) % N).
};
static std::unique_ptr<ScopedMessageBoxInterface> makeNativeMessageBoxWithMappedResult (const MessageBoxOptions& opts,
ResultCodeMappingMode mode)
static std::unique_ptr<detail::ScopedMessageBoxInterface> makeNativeMessageBoxWithMappedResult (const MessageBoxOptions& opts,
ResultCodeMappingMode mode)
{
class Adapter : public ScopedMessageBoxInterface
class Adapter : public detail::ScopedMessageBoxInterface
{
public:
explicit Adapter (const MessageBoxOptions& options)
: inner (ScopedMessageBoxInterface::create (options)),
: inner (detail::ScopedMessageBoxInterface::create (options)),
numButtons (options.getNumButtons()) {}
void runAsync (std::function<void (int)> fn) override
@ -65,11 +65,11 @@ static std::unique_ptr<ScopedMessageBoxInterface> makeNativeMessageBoxWithMapped
private:
static int map (int button, int numButtons) { return (button + 1) % numButtons; }
std::unique_ptr<ScopedMessageBoxInterface> inner;
std::unique_ptr<detail::ScopedMessageBoxInterface> inner;
int numButtons = 0;
};
return mode == ResultCodeMappingMode::plainIndex ? ScopedMessageBoxInterface::create (opts)
return mode == ResultCodeMappingMode::plainIndex ? detail::ScopedMessageBoxInterface::create (opts)
: std::make_unique<Adapter> (opts);
}
@ -78,7 +78,7 @@ static int showNativeBoxUnmanaged (const MessageBoxOptions& opts,
ResultCodeMappingMode mode)
{
auto implementation = makeNativeMessageBoxWithMappedResult (opts, mode);
return ScopedMessageBox::Pimpl::showUnmanaged (std::move (implementation), cb);
return detail::ScopedMessageBoxImpl::showUnmanaged (std::move (implementation), cb);
}
#if JUCE_MODAL_LOOPS_PERMITTED
@ -152,7 +152,7 @@ void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options
ScopedMessageBox NativeMessageBox::showScopedAsync (const MessageBoxOptions& options, std::function<void (int)> callback)
{
auto implementation = makeNativeMessageBoxWithMappedResult (options, ResultCodeMappingMode::alertWindow);
return ScopedMessageBox::Pimpl::show (std::move (implementation), std::move (callback));
return detail::ScopedMessageBoxImpl::show (std::move (implementation), std::move (callback));
}
} // namespace juce

View file

@ -26,134 +26,11 @@
namespace juce
{
/*
Instances of this type can show and dismiss a message box.
This is an interface rather than a concrete type so that platforms can pick an implementation at
runtime if necessary.
*/
struct ScopedMessageBoxInterface
{
virtual ~ScopedMessageBoxInterface() = default;
/* Shows the message box.
When the message box exits normally, it should send the result to the passed-in function.
The passed-in function is safe to call from any thread at any time.
*/
virtual void runAsync (std::function<void (int)>) = 0;
/* Shows the message box and blocks. */
virtual int runSync() = 0;
/* Forcefully closes the message box.
This will be called when the message box handle has fallen out of scope.
If the message box has already been closed by the user, this shouldn't do anything.
*/
virtual void close() = 0;
/* Implemented differently for each platform. */
static std::unique_ptr<ScopedMessageBoxInterface> create (const MessageBoxOptions& options);
};
//==============================================================================
class ScopedMessageBox::Pimpl : private AsyncUpdater
{
public:
static ScopedMessageBox show (std::unique_ptr<ScopedMessageBoxInterface>&& native, std::function<void (int)> callback)
{
return ScopedMessageBox (runAsync (std::move (native), rawToUniquePtr (ModalCallbackFunction::create (std::move (callback)))));
}
static int showUnmanaged (std::unique_ptr<ScopedMessageBoxInterface>&& native,
ModalComponentManager::Callback* cb)
{
#if JUCE_MODAL_LOOPS_PERMITTED
if (cb == nullptr)
return runSync (std::move (native));
#endif
runAsync (std::move (native), rawToUniquePtr (cb));
return 0;
}
~Pimpl() override
{
cancelPendingUpdate();
}
void close()
{
cancelPendingUpdate();
nativeImplementation->close();
self.reset();
}
private:
static std::shared_ptr<Pimpl> runAsync (std::unique_ptr<ScopedMessageBoxInterface>&& p,
std::unique_ptr<ModalComponentManager::Callback>&& c)
{
std::shared_ptr<Pimpl> result (new Pimpl (std::move (p), std::move (c)));
result->self = result;
result->triggerAsyncUpdate();
return result;
}
static int runSync (std::unique_ptr<ScopedMessageBoxInterface>&& p)
{
auto local = std::move (p);
return local != nullptr ? local->runSync() : 0;
}
explicit Pimpl (std::unique_ptr<ScopedMessageBoxInterface>&& p)
: Pimpl (std::move (p), nullptr) {}
Pimpl (std::unique_ptr<ScopedMessageBoxInterface>&& p,
std::unique_ptr<ModalComponentManager::Callback>&& c)
: callback (std::move (c)), nativeImplementation (std::move (p)) {}
void handleAsyncUpdate() override
{
nativeImplementation->runAsync ([weakRecipient = std::weak_ptr<Pimpl> (self)] (int result)
{
const auto notifyRecipient = [result, weakRecipient]
{
if (const auto locked = weakRecipient.lock())
{
if (auto* cb = locked->callback.get())
cb->modalStateFinished (result);
locked->self.reset();
}
};
if (MessageManager::getInstance()->isThisTheMessageThread())
notifyRecipient();
else
MessageManager::callAsync (notifyRecipient);
});
}
std::unique_ptr<ModalComponentManager::Callback> callback;
std::unique_ptr<ScopedMessageBoxInterface> nativeImplementation;
/* The 'old' native message box API doesn't have a concept of message box owners.
Instead, message boxes have to clean up after themselves, once they're done displaying.
To allow this mode of usage, the implementation keeps an owning reference to itself,
which is cleared once the message box is closed or asked to quit. To display a native
message box without a scoped lifetime, just create a Pimpl instance without using
the ScopedMessageBox wrapper, and the Pimpl will destroy itself after it is dismissed.
*/
std::shared_ptr<Pimpl> self;
};
//==============================================================================
ScopedMessageBox::ScopedMessageBox() = default;
ScopedMessageBox::ScopedMessageBox (std::shared_ptr<Pimpl> p)
: pimpl (std::move (p)) {}
ScopedMessageBox::ScopedMessageBox (std::shared_ptr<detail::ScopedMessageBoxImpl> i)
: impl (std::move (i)) {}
ScopedMessageBox::~ScopedMessageBox() noexcept
{
@ -161,21 +38,21 @@ ScopedMessageBox::~ScopedMessageBox() noexcept
}
ScopedMessageBox::ScopedMessageBox (ScopedMessageBox&& other) noexcept
: pimpl (std::exchange (other.pimpl, nullptr)) {}
: impl (std::exchange (other.impl, nullptr)) {}
ScopedMessageBox& ScopedMessageBox::operator= (ScopedMessageBox&& other) noexcept
{
ScopedMessageBox temp (std::move (other));
std::swap (temp.pimpl, pimpl);
std::swap (temp.impl, impl);
return *this;
}
void ScopedMessageBox::close()
{
if (pimpl != nullptr)
pimpl->close();
if (impl != nullptr)
impl->close();
pimpl.reset();
impl.reset();
}
} // namespace juce

View file

@ -55,13 +55,11 @@ public:
*/
void close();
/** @internal */
class Pimpl;
private:
explicit ScopedMessageBox (std::shared_ptr<Pimpl>);
friend detail::ScopedMessageBoxImpl;
explicit ScopedMessageBox (std::shared_ptr<detail::ScopedMessageBoxImpl>);
std::shared_ptr<Pimpl> pimpl;
std::shared_ptr<detail::ScopedMessageBoxImpl> impl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedMessageBox)
};

View file

@ -116,8 +116,8 @@ void TooltipWindow::displayTipInternal (Point<int> screenPos, const String& tip,
}
else
{
const auto physicalPos = ScalingHelpers::scaledScreenPosToUnscaled (screenPos);
const auto scaledPos = ScalingHelpers::unscaledScreenPosToScaled (*this, physicalPos);
const auto physicalPos = detail::ScalingHelpers::scaledScreenPosToUnscaled (screenPos);
const auto scaledPos = detail::ScalingHelpers::unscaledScreenPosToScaled (*this, physicalPos);
updatePosition (tip, scaledPos, Desktop::getInstance().getDisplays().getDisplayForPoint (screenPos)->userArea);
addToDesktop (ComponentPeer::windowHasDropShadow
@ -151,7 +151,7 @@ void TooltipWindow::displayTipInternal (Point<int> screenPos, const String& tip,
String TooltipWindow::getTipFor (Component& c)
{
if (isForegroundOrEmbeddedProcess (&c)
if (detail::WindowingHelpers::isForegroundOrEmbeddedProcess (&c)
&& ! ModifierKeys::currentModifiers.isAnyMouseButtonDown())
{
if (auto* ttc = dynamic_cast<TooltipClient*> (&c))

View file

@ -26,110 +26,6 @@
namespace juce
{
/** Keeps track of the active top level window. */
class TopLevelWindowManager : private Timer,
private DeletedAtShutdown
{
public:
TopLevelWindowManager() {}
~TopLevelWindowManager() override { clearSingletonInstance(); }
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (TopLevelWindowManager)
void checkFocusAsync()
{
startTimer (10);
}
void checkFocus()
{
startTimer (jmin (1731, getTimerInterval() * 2));
auto* newActive = findCurrentlyActiveWindow();
if (newActive != currentActive)
{
currentActive = newActive;
for (int i = windows.size(); --i >= 0;)
if (auto* tlw = windows[i])
tlw->setWindowActive (isWindowActive (tlw));
Desktop::getInstance().triggerFocusCallback();
}
}
bool addWindow (TopLevelWindow* const w)
{
windows.add (w);
checkFocusAsync();
return isWindowActive (w);
}
void removeWindow (TopLevelWindow* const w)
{
checkFocusAsync();
if (currentActive == w)
currentActive = nullptr;
windows.removeFirstMatchingValue (w);
if (windows.isEmpty())
deleteInstance();
}
Array<TopLevelWindow*> windows;
private:
TopLevelWindow* currentActive = nullptr;
void timerCallback() override
{
checkFocus();
}
bool isWindowActive (TopLevelWindow* const tlw) const
{
return (tlw == currentActive
|| tlw->isParentOf (currentActive)
|| tlw->hasKeyboardFocus (true))
&& tlw->isShowing();
}
TopLevelWindow* findCurrentlyActiveWindow() const
{
if (Process::isForegroundProcess())
{
auto* focusedComp = Component::getCurrentlyFocusedComponent();
auto* w = dynamic_cast<TopLevelWindow*> (focusedComp);
if (w == nullptr && focusedComp != nullptr)
w = focusedComp->findParentComponentOfClass<TopLevelWindow>();
if (w == nullptr)
w = currentActive;
if (w != nullptr && w->isShowing())
return w;
}
return nullptr;
}
JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager)
};
JUCE_IMPLEMENT_SINGLETON (TopLevelWindowManager)
void juce_checkCurrentlyFocusedTopLevelWindow();
void juce_checkCurrentlyFocusedTopLevelWindow()
{
if (auto* wm = TopLevelWindowManager::getInstanceWithoutCreating())
wm->checkFocusAsync();
}
//==============================================================================
TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDesktop)
: Component (name)
@ -145,19 +41,19 @@ TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDeskto
setWantsKeyboardFocus (true);
setBroughtToFrontOnMouseClick (true);
isCurrentlyActive = TopLevelWindowManager::getInstance()->addWindow (this);
isCurrentlyActive = detail::TopLevelWindowManager::getInstance()->addWindow (this);
}
TopLevelWindow::~TopLevelWindow()
{
shadower = nullptr;
TopLevelWindowManager::getInstance()->removeWindow (this);
detail::TopLevelWindowManager::getInstance()->removeWindow (this);
}
//==============================================================================
void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType)
{
auto* wm = TopLevelWindowManager::getInstance();
auto* wm = detail::TopLevelWindowManager::getInstance();
if (hasKeyboardFocus (true))
wm->checkFocus();
@ -239,7 +135,7 @@ void TopLevelWindow::setUsingNativeTitleBar (const bool shouldUseNativeTitleBar)
{
if (useNativeTitleBar != shouldUseNativeTitleBar)
{
FocusRestorer focusRestorer;
detail::FocusRestorer focusRestorer;
useNativeTitleBar = shouldUseNativeTitleBar;
recreateDesktopWindow();
sendLookAndFeelChange();
@ -320,12 +216,12 @@ void TopLevelWindow::centreAroundComponent (Component* c, const int width, const
//==============================================================================
int TopLevelWindow::getNumTopLevelWindows() noexcept
{
return TopLevelWindowManager::getInstance()->windows.size();
return detail::TopLevelWindowManager::getInstance()->windows.size();
}
TopLevelWindow* TopLevelWindow::getTopLevelWindow (const int index) noexcept
{
return TopLevelWindowManager::getInstance()->windows [index];
return detail::TopLevelWindowManager::getInstance()->windows [index];
}
TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() noexcept

View file

@ -154,7 +154,7 @@ protected:
void visibilityChanged() override;
private:
friend class TopLevelWindowManager;
friend class detail::TopLevelWindowManager;
friend class ResizableWindow;
bool useDropShadow = true, useNativeTitleBar = false, isCurrentlyActive = false;
std::unique_ptr<DropShadower> shadower;