mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
SidePanel: Add an option to restrict content to the safe screen area
A major benefit of this change is that the menu in the DemoRunner will now display reasonably on mobile devices with notches or other decorations.
This commit is contained in:
parent
f30d70049c
commit
cfc006aaf9
4 changed files with 70 additions and 8 deletions
|
|
@ -308,6 +308,7 @@ MainComponent::MainComponent()
|
||||||
|
|
||||||
demosPanel.setTitle ("Demos");
|
demosPanel.setTitle ("Demos");
|
||||||
demosPanel.setFocusContainerType (FocusContainerType::focusContainer);
|
demosPanel.setFocusContainerType (FocusContainerType::focusContainer);
|
||||||
|
demosPanel.setContentRestrictedToSafeArea (true);
|
||||||
|
|
||||||
showDemosButton.onClick = [this] { demosPanel.showOrHide (true); };
|
showDemosButton.onClick = [this] { demosPanel.showOrHide (true); };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,19 +56,41 @@ public:
|
||||||
|
|
||||||
/** The total area of this display in logical pixels including any OS-dependent objects
|
/** The total area of this display in logical pixels including any OS-dependent objects
|
||||||
like the taskbar, menu bar, etc.
|
like the taskbar, menu bar, etc.
|
||||||
|
|
||||||
|
On mobile (Android, iOS) this is the full area of the display.
|
||||||
*/
|
*/
|
||||||
Rectangle<int> totalArea;
|
Rectangle<int> totalArea;
|
||||||
|
|
||||||
/** The total area of this display in logical pixels which isn't covered by OS-dependent
|
/** The total area of this display in logical pixels which isn't covered by OS-dependent
|
||||||
objects like the taskbar, menu bar, etc.
|
objects like the taskbar, menu bar, etc.
|
||||||
|
|
||||||
|
On mobile (iOS, Android), the system UI will be made transparent whenever possible, and
|
||||||
|
the JUCE app may draw behind these bars. Therefore, on these platforms, the userArea
|
||||||
|
is *not* restricted by the system UI. Instead, potentially-obscured areas of the
|
||||||
|
display can be found by querying the safeAreaInsets and keyboardInsets.
|
||||||
|
|
||||||
|
Mobile platforms that support multiple windows (e.g. Android in split screen) will
|
||||||
|
return the screen area currently available to the application here. The resulting
|
||||||
|
area may be significantly smaller than the total screen area, but may overlap the
|
||||||
|
system decorations.
|
||||||
*/
|
*/
|
||||||
Rectangle<int> userArea;
|
Rectangle<int> userArea;
|
||||||
|
|
||||||
/** Represents the area of this display in logical pixels that is not functional for
|
/** Represents the area of this display in logical pixels that is not functional for
|
||||||
displaying content.
|
displaying content.
|
||||||
|
|
||||||
|
These insets are applied relative to the userArea.
|
||||||
|
|
||||||
On mobile devices this may be the area covered by display cutouts and notches, where
|
On mobile devices this may be the area covered by display cutouts and notches, where
|
||||||
you still want to draw a background but should not position important content.
|
you still want to draw a background but should not position important content.
|
||||||
|
|
||||||
|
Note that these insets may change depending on the current state of the system.
|
||||||
|
As a simple example, entering/leaving kiosk mode may cause the system UI visibility
|
||||||
|
to change, which may affect the safe areas.
|
||||||
|
A more complex example would be split-screen state on Android, where an activity
|
||||||
|
occupying the top portion of the screen is likely to have insets for the status bar but
|
||||||
|
not the navigation bar, whereas an activity on the bottom may have navigation insets
|
||||||
|
but not status insets.
|
||||||
*/
|
*/
|
||||||
BorderSize<int> safeAreaInsets;
|
BorderSize<int> safeAreaInsets;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,24 @@ void SidePanel::resized()
|
||||||
|
|
||||||
calculateAndRemoveShadowBounds (bounds);
|
calculateAndRemoveShadowBounds (bounds);
|
||||||
|
|
||||||
|
const auto fullScreen = std::invoke ([&]
|
||||||
|
{
|
||||||
|
if (auto* peer = getPeer())
|
||||||
|
return peer->isFullScreen();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fullScreen && isContentRestrictedToSafeArea() && parent != nullptr)
|
||||||
|
{
|
||||||
|
if (auto* display = Desktop::getInstance().getDisplays().getDisplayForRect (parent->getScreenBounds()))
|
||||||
|
{
|
||||||
|
const auto safeArea = display->safeAreaInsets.subtractedFrom (display->keyboardInsets.subtractedFrom (display->userArea));
|
||||||
|
const auto safeAreaInLocalSpace = getLocalArea (nullptr, safeArea) + getCurrentOffset();
|
||||||
|
bounds = bounds.getIntersection (safeAreaInLocalSpace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto titleBounds = bounds.removeFromTop (titleBarHeight);
|
auto titleBounds = bounds.removeFromTop (titleBarHeight);
|
||||||
|
|
||||||
if (titleBarComponent != nullptr)
|
if (titleBarComponent != nullptr)
|
||||||
|
|
@ -268,18 +286,25 @@ void SidePanel::changeListenerCallback (ChangeBroadcaster*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
|
Rectangle<int> SidePanel::calculateShowingBoundsInParent (Component& parentComp) const
|
||||||
{
|
{
|
||||||
auto parentBounds = parentComp.getLocalBounds();
|
auto parentBounds = parentComp.getLocalBounds();
|
||||||
|
|
||||||
if (isOnLeft)
|
return isOnLeft ? parentBounds.removeFromLeft (panelWidth)
|
||||||
{
|
: parentBounds.removeFromRight (panelWidth);
|
||||||
return isShowing ? parentBounds.removeFromLeft (panelWidth)
|
}
|
||||||
: parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isShowing ? parentBounds.removeFromRight (panelWidth)
|
Point<int> SidePanel::getCurrentOffset() const
|
||||||
: parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
|
{
|
||||||
|
if (isShowing)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return { isOnLeft ? -panelWidth : panelWidth, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
|
||||||
|
{
|
||||||
|
return calculateShowingBoundsInParent (parentComp) + getCurrentOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
|
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,17 @@ public:
|
||||||
/** Returns the text that is displayed in the title bar at the top of the SidePanel. */
|
/** Returns the text that is displayed in the title bar at the top of the SidePanel. */
|
||||||
String getTitleText() const noexcept { return titleLabel.getText(); }
|
String getTitleText() const noexcept { return titleLabel.getText(); }
|
||||||
|
|
||||||
|
/** @see isContentRestrictedToSafeArea() */
|
||||||
|
void setContentRestrictedToSafeArea (bool x) noexcept { restrictToSafeArea = x; }
|
||||||
|
|
||||||
|
/** When true, will avoid displaying menu content within areas of the screen that may be
|
||||||
|
obscured by display cutouts or operating system decorations. When false, the menu's
|
||||||
|
content will entirely fill the menu bounds. True by default.
|
||||||
|
|
||||||
|
@see setContentRestrictedToSafeArea()
|
||||||
|
*/
|
||||||
|
bool isContentRestrictedToSafeArea() const noexcept { return restrictToSafeArea; }
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||||
SidePanel drawing functionality.
|
SidePanel drawing functionality.
|
||||||
|
|
@ -230,12 +241,15 @@ private:
|
||||||
int amountMoved = 0;
|
int amountMoved = 0;
|
||||||
|
|
||||||
bool shouldShowDismissButton = true;
|
bool shouldShowDismissButton = true;
|
||||||
|
bool restrictToSafeArea = true;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void lookAndFeelChanged() override;
|
void lookAndFeelChanged() override;
|
||||||
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
|
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
|
||||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||||
|
|
||||||
|
Rectangle<int> calculateShowingBoundsInParent (Component&) const;
|
||||||
|
Point<int> getCurrentOffset() const;
|
||||||
Rectangle<int> calculateBoundsInParent (Component&) const;
|
Rectangle<int> calculateBoundsInParent (Component&) const;
|
||||||
void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
|
void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue