diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt
index 9603342672..f86cffe35a 100644
--- a/Common/CMakeLists.txt
+++ b/Common/CMakeLists.txt
@@ -278,6 +278,7 @@ add_library(Common STATIC
Render/TextureAtlas.h
Render/DrawBuffer.cpp
Render/DrawBuffer.h
+ Render/DrawEnum.h
Render/ManagedTexture.cpp
Render/ManagedTexture.h
Render/Text/Font.h
diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj
index 4169ccbafc..f618df14f5 100644
--- a/Common/Common.vcxproj
+++ b/Common/Common.vcxproj
@@ -495,6 +495,7 @@
+
@@ -1133,4 +1134,4 @@
-
+
\ No newline at end of file
diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters
index 8780958f1d..ee58c22799 100644
--- a/Common/Common.vcxproj.filters
+++ b/Common/Common.vcxproj.filters
@@ -818,6 +818,9 @@
UI
+
+ Render
+
@@ -1709,4 +1712,4 @@
Data\Collections
-
+
\ No newline at end of file
diff --git a/Common/Render/DrawBuffer.h b/Common/Render/DrawBuffer.h
index 1bd6751e91..a5ffbf4c4e 100644
--- a/Common/Render/DrawBuffer.h
+++ b/Common/Render/DrawBuffer.h
@@ -5,7 +5,6 @@
#include
#include
#include
-#include
#include "Common/Common.h"
#include "Common/Data/Color/RGBAUtil.h"
@@ -14,34 +13,10 @@
#include "Common/Math/lin/matrix4x4.h"
#include "Common/Math/math_util.h"
#include "Common/GPU/thin3d.h"
+#include "Common/Render/DrawEnum.h"
struct Atlas;
-enum {
- ALIGN_LEFT = 0,
- ALIGN_RIGHT = 16,
- ALIGN_TOP = 0,
- ALIGN_BOTTOM = 1,
- ALIGN_HCENTER = 4,
- ALIGN_VCENTER = 8,
- ALIGN_VBASELINE = 32, // text only, possibly not yet working
-
- ALIGN_CENTER = ALIGN_HCENTER | ALIGN_VCENTER,
- ALIGN_TOPLEFT = ALIGN_TOP | ALIGN_LEFT,
- ALIGN_TOPRIGHT = ALIGN_TOP | ALIGN_RIGHT,
- ALIGN_BOTTOMLEFT = ALIGN_BOTTOM | ALIGN_LEFT,
- ALIGN_BOTTOMRIGHT = ALIGN_BOTTOM | ALIGN_RIGHT,
-
- // For "uncachable" text like debug log.
- // Avoids using system font drawing as it's too slow.
- // Not actually used here but is reserved for whatever system wraps DrawBuffer.
- FLAG_DYNAMIC_ASCII = 2048,
- FLAG_NO_PREFIX = 4096, // means to not process ampersands
- FLAG_WRAP_TEXT = 8192,
- FLAG_ELLIPSIZE_TEXT = 16384,
- FLAG_DROP_SHADOW = 32768, // soft shadow under text (system font drawing only)
-};
-
namespace Draw {
class Pipeline;
}
@@ -53,38 +28,6 @@ struct GradientStop {
class TextDrawer;
-// Rounded rectangles are drawn from antialiased corner images that UI/UIAtlas.cpp generates
-// into the UI atlas, one per radius and style.
-enum class RoundRectStyle : uint8_t {
- Filled,
- OutlineThin,
- OutlineMedium,
- OutlineThick,
- COUNT,
-};
-
-// Corner radii in UI units, each of which gets an image per style. Other radii scale the nearest one.
-// Keep them larger than the thickest outline.
-constexpr float ROUND_RECT_RADII[] = { 8.0f, 16.0f, 24.0f, 32.0f };
-constexpr int ROUND_RECT_RADIUS_COUNT = (int)std::size(ROUND_RECT_RADII);
-// Outline thickness in UI units, per RoundRectStyle.
-constexpr float ROUND_RECT_THICKNESS[(int)RoundRectStyle::COUNT] = { 0.0f, 1.0f, 2.0f, 3.0f };
-
-// Which corners of a round rect to round. The others are drawn square.
-enum class RoundCorners : uint8_t {
- None = 0,
- TopLeft = 1,
- TopRight = 2,
- BottomLeft = 4,
- BottomRight = 8,
- Top = 3, // TopLeft | TopRight
- Bottom = 12, // BottomLeft | BottomRight
- Left = 5, // TopLeft | BottomLeft
- Right = 10, // TopRight | BottomRight
- All = 15,
-};
-ENUM_CLASS_BITOPS(RoundCorners);
-
const char *RoundRectCornerImageName(int radiusIndex, RoundRectStyle style);
// The corner image is (center + 1) texels square. The arc is centered on texel corner (center, center),
// and the last row and column are the straight edge profile.
diff --git a/Common/Render/DrawEnum.h b/Common/Render/DrawEnum.h
new file mode 100644
index 0000000000..1a8f1441d5
--- /dev/null
+++ b/Common/Render/DrawEnum.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include
+#include "Common/Common.h"
+
+enum {
+ ALIGN_LEFT = 0,
+ ALIGN_RIGHT = 16,
+ ALIGN_TOP = 0,
+ ALIGN_BOTTOM = 1,
+ ALIGN_HCENTER = 4,
+ ALIGN_VCENTER = 8,
+ ALIGN_VBASELINE = 32, // text only, possibly not yet working
+
+ ALIGN_CENTER = ALIGN_HCENTER | ALIGN_VCENTER,
+ ALIGN_TOPLEFT = ALIGN_TOP | ALIGN_LEFT,
+ ALIGN_TOPRIGHT = ALIGN_TOP | ALIGN_RIGHT,
+ ALIGN_BOTTOMLEFT = ALIGN_BOTTOM | ALIGN_LEFT,
+ ALIGN_BOTTOMRIGHT = ALIGN_BOTTOM | ALIGN_RIGHT,
+
+ // For "uncachable" text like debug log.
+ // Avoids using system font drawing as it's too slow.
+ // Not actually used here but is reserved for whatever system wraps DrawBuffer.
+ FLAG_DYNAMIC_ASCII = 2048,
+ FLAG_NO_PREFIX = 4096, // means to not process ampersands
+ FLAG_WRAP_TEXT = 8192,
+ FLAG_ELLIPSIZE_TEXT = 16384,
+ FLAG_DROP_SHADOW = 32768, // soft shadow under text (system font drawing only)
+};
+
+// Rounded rectangles are drawn from antialiased corner images that UI/UIAtlas.cpp generates
+// into the UI atlas, one per radius and style.
+enum class RoundRectStyle : uint8_t {
+ Filled,
+ OutlineThin,
+ OutlineMedium,
+ OutlineThick,
+ COUNT,
+};
+
+// Corner radii in UI units, each of which gets an image per style. Other radii scale the nearest one.
+// Keep them larger than the thickest outline.
+constexpr float ROUND_RECT_RADII[] = {8.0f, 16.0f, 24.0f, 32.0f};
+constexpr int ROUND_RECT_RADIUS_COUNT = ARRAY_SIZE(ROUND_RECT_RADII);
+// Outline thickness in UI units, per RoundRectStyle.
+constexpr float ROUND_RECT_THICKNESS[(int)RoundRectStyle::COUNT] = {0.0f, 1.0f, 2.0f, 3.0f};
+
+// Which corners of a round rect to round. The others are drawn square.
+enum class RoundCorners : uint8_t {
+ None = 0,
+ TopLeft = 1,
+ TopRight = 2,
+ BottomLeft = 4,
+ BottomRight = 8,
+ Top = 3, // TopLeft | TopRight
+ Bottom = 12, // BottomLeft | BottomRight
+ Left = 5, // TopLeft | BottomLeft
+ Right = 10, // TopRight | BottomRight
+ All = 15,
+};
+ENUM_CLASS_BITOPS(RoundCorners);
diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp
index b44146eed2..86d5d8acbc 100644
--- a/Common/UI/PopupScreens.cpp
+++ b/Common/UI/PopupScreens.cpp
@@ -232,6 +232,9 @@ void PopupScreen::CreateViews() {
box_->Add(buttonRow);
}
+
+ // The popup layout isn't compatible with round corners.
+ box_->ApplyRoundCorners(RoundCorners::None);
}
void MessagePopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
diff --git a/Common/UI/TabHolder.cpp b/Common/UI/TabHolder.cpp
index 32129466d9..ace2b50b77 100644
--- a/Common/UI/TabHolder.cpp
+++ b/Common/UI/TabHolder.cpp
@@ -81,6 +81,8 @@ void TabHolder::AddTabContents(std::string_view title, ImageID imageId, ViewGrou
} else {
tabStrip_->AddChoice(title);
}
+ tabStrip_->ApplyRoundCorners(orientation_ == Orientation::ORIENT_HORIZONTAL ? RoundCorners::Left : RoundCorners::Top);
+
contents_->Add(tabContents);
if (tabs_.size() > 1)
tabContents->SetVisibility(V_GONE);
@@ -101,6 +103,8 @@ void TabHolder::AddTabDeferred(std::string_view title, ImageID imageId, std::fun
} else {
tabStrip_->AddChoice(title);
}
+ tabStrip_->ApplyRoundCorners(orientation_ == Orientation::ORIENT_HORIZONTAL ? RoundCorners::Left : RoundCorners::Top);
+
tabTweens_.push_back(nullptr);
createFuncs_.push_back(createCb);
}
diff --git a/Common/UI/View.h b/Common/UI/View.h
index 2a779570b1..abff091b42 100644
--- a/Common/UI/View.h
+++ b/Common/UI/View.h
@@ -24,6 +24,7 @@
#include "Common/Common.h"
#include "Common/UI/Screen.h" // for DialogResult
+#include "Common/Render/DrawEnum.h" // for RoundCorners
#undef small
@@ -527,7 +528,7 @@ protected:
bool popupStyle_ = false;
bool hasAutoResult_ = false;
DialogResult autoResult_ = DR_OK;
- RoundCorners roundCorners_{};
+ RoundCorners roundCorners_ = RoundCorners::All;
private:
friend class ViewGroup; // Sets parent_.
@@ -766,6 +767,7 @@ public:
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
+
std::string DescribeText() const override;
void SetCentered(bool c) {
centered_ = c;
@@ -783,6 +785,7 @@ public:
rightIconFlipH_ = flipH;
rightIconImage_ = iconImage;
}
+
// Only useful for things that extend Choice.
void SetHideTitle(bool hide) {
hideTitle_ = hide;
diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp
index dcab250adc..5b90b03333 100644
--- a/Common/UI/ViewGroup.cpp
+++ b/Common/UI/ViewGroup.cpp
@@ -811,6 +811,29 @@ void LinearLayout::Layout() {
}
}
+// Applies rounding to the corners of the child views, based on their position in the layout and the orientation. Masked by the outerCorners.
+// Ignores invisible views.
+void LinearLayout::ApplyRoundCorners(RoundCorners outerCorners) {
+ bool seenVisible = false;
+ View *lastVisible = nullptr;
+ for (View *view : views_) {
+ if (view->GetVisibility() == V_GONE) {
+ continue;
+ }
+ if (!seenVisible) {
+ // First visible view, round the leading edge.
+ view->SetRoundCorners((RoundCorners)((u32)(orientation_ == ORIENT_HORIZONTAL ? RoundCorners::Left : RoundCorners::Top) & (u32)outerCorners));
+ seenVisible = true;
+ } else {
+ view->SetRoundCorners(RoundCorners::None);
+ }
+ lastVisible = view;
+ }
+ if (lastVisible) {
+ lastVisible->SetRoundCorners(lastVisible->GetRoundCorners() | (RoundCorners)((u32)(orientation_ == ORIENT_HORIZONTAL ? RoundCorners::Right : RoundCorners::Bottom) & (u32)outerCorners));
+ }
+}
+
std::string LinearLayoutList::DescribeText() const {
auto u = GetI18NCategory(I18NCat::UI_ELEMENTS);
return DescribeListOrdered(u->T("List:"));
@@ -831,8 +854,7 @@ void ChoiceGroup::Draw(UIContext &dc) {
}
if (!finished_) {
- views_[0]->SetRoundCorners(RoundCorners::Top);
- views_[views_.size() - 1]->SetRoundCorners(views_[views_.size() - 1]->GetRoundCorners() | RoundCorners::Bottom); // Makes it work with a single item.
+ ApplyRoundCorners(RoundCorners::All);
finished_ = true;
}
diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h
index 491c8e16bd..fa4d243145 100644
--- a/Common/UI/ViewGroup.h
+++ b/Common/UI/ViewGroup.h
@@ -8,6 +8,7 @@
#include "Common/Math/geom2d.h"
#include "Common/Input/GestureDetector.h"
#include "Common/UI/View.h"
+#include "Common/Render/DrawEnum.h"
class UIScreen;
@@ -226,6 +227,10 @@ public:
void SetSpacing(float spacing) {
spacing_ = spacing;
}
+
+ // Applies rounding to the corners of the child views, based on their position in the layout and the orientation. Masked by the outerCorners.
+ void ApplyRoundCorners(RoundCorners outerCorners = RoundCorners::All);
+
std::string DescribeLog() const override { return (orientation_ == ORIENT_HORIZONTAL ? "LinearLayoutHoriz: " : "LinearLayoutVert: ") + View::DescribeLog(); }
Margins padding;
diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp
index 75681fbf97..a8e33aa725 100644
--- a/UI/ControlMappingScreen.cpp
+++ b/UI/ControlMappingScreen.cpp
@@ -227,26 +227,31 @@ static const BindingCategory cats[] = {
{}, // sentinel
};
-
void ControlMappingScreen::CreateSettingsViews(UI::ViewGroup *parent) {
using namespace UI;
auto km = GetI18NCategory(I18NCat::KEYMAPPING);
- parent->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) {
+
+ ChoiceGroup *resetChoices = parent->Add(new ChoiceGroup("", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(0, 12))));
+
+ resetChoices->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) {
KeyMap::ClearAllMappings();
});
- parent->Add(new Choice(km->T("Default All")))->OnClick.Add([](UI::EventParams &) {
+ resetChoices->Add(new Choice(km->T("Default All")))->OnClick.Add([](UI::EventParams &) {
KeyMap::RestoreDefault();
});
+
+ ChoiceGroup *moreChoices = parent->Add(new ChoiceGroup("", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
+
std::string sysName = System_GetProperty(SYSPROP_NAME);
// If there's a builtin controller, restore to default should suffice. No need to conf the controller on top.
if (!KeyMap::HasBuiltinController(sysName) && KeyMap::GetSeenPads().size()) {
- parent->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure);
+ moreChoices->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure);
}
- parent->Add(new Choice(km->T("Show PSP")))->OnClick.Add([this](UI::EventParams ¶ms) {
+ moreChoices->Add(new Choice(km->T("Show PSP")))->OnClick.Add([this](UI::EventParams ¶ms) {
screenManager()->push(new VisualMappingScreen(gamePath_));
});
- parent->Add(new CheckBox(&g_Config.bAllowMappingCombos, km->T("Allow combo mappings")));
- parent->Add(new CheckBox(&g_Config.bStrictComboOrder, km->T("Strict combo input order")));
+ moreChoices->Add(new CheckBox(&g_Config.bAllowMappingCombos, km->T("Allow combo mappings")));
+ moreChoices->Add(new CheckBox(&g_Config.bStrictComboOrder, km->T("Strict combo input order")));
}
std::string_view ControlMappingScreen::GetTitle() const {
diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp
index 288f075283..a36f0da446 100644
--- a/UI/DisplayLayoutScreen.cpp
+++ b/UI/DisplayLayoutScreen.cpp
@@ -324,7 +324,7 @@ void DisplayLayoutScreen::CreateViews() {
rightColumn->Add(new ItemHeader(gr->T("PSP display rotation")));
static const char *displayRotation[] = { "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed" };
- auto rotation = new PopupMultiChoice(&config.iInternalScreenRotation, gr->T("Rotation"), displayRotation, 1, ARRAY_SIZE(displayRotation), I18NCat::CONTROLS, screenManager());
+ PopupMultiChoice *rotation = rightColumn->Add(new PopupMultiChoice(&config.iInternalScreenRotation, gr->T("Rotation"), displayRotation, 1, ARRAY_SIZE(displayRotation), I18NCat::CONTROLS, screenManager()));
rotation->OnChoice.Add([this](UI::EventParams &) {
// Affects the presence of the Stretch checkbox.
RecreateViews();
@@ -334,8 +334,6 @@ void DisplayLayoutScreen::CreateViews() {
});
rotation->SetHideTitle(true);
-
- rightColumn->Add(rotation);
rightColumn->Add(new CheckBox(&config.bRotateControlsWithScreen, gr->T("Rotate controls")))->SetEnabledFunc([&config]() -> bool {
return (!g_Config.bSkipBufferEffects || g_Config.bSoftwareRendering) && config.iInternalScreenRotation != 1;
});
diff --git a/UI/GameBrowser.cpp b/UI/GameBrowser.cpp
index f21ed1ea52..e9d886fcff 100644
--- a/UI/GameBrowser.cpp
+++ b/UI/GameBrowser.cpp
@@ -1095,6 +1095,7 @@ void GameBrowser::Refresh() {
layoutChoice->AddChoice(ImageID("I_LINES"));
layoutChoice->AddChoice(ImageID("I_LARGE_LIST"));
layoutChoice->SetSelection((int)lastViewMode_, false);
+ layoutChoice->ApplyRoundCorners();
layoutChoice->OnChoice.Handle(this, &GameBrowser::LayoutChange);
if (!isRecent) {
topBar->Add(new Choice(ImageID("I_ROTATE_LEFT"), new LayoutParams(64.0f, 64.0f)))->OnClick.Add([=](UI::EventParams &e) {
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index 2fffa45d70..42cfd23290 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -649,15 +649,17 @@ void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
auto di = GetI18NCategory(I18NCat::DIALOG);
+#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
+ ChoiceGroup *systemIntegrationSettings = audioSettings->Add(new ChoiceGroup("")); // Don't need a title.
#if PPSSPP_PLATFORM(IOS)
- CheckBox *respectSilentMode = audioSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));
+ CheckBox *respectSilentMode = systemIntegrationSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));
respectSilentMode->OnClick.Add([=](EventParams &e) {
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
});
respectSilentMode->SetEnabledPtr(&g_Config.bEnableSound);
#endif
-#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
- CheckBox *mixWithOthers = audioSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));
+
+ CheckBox *mixWithOthers = systemIntegrationSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));
mixWithOthers->OnClick.Add([=](EventParams &e) {
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
});
@@ -703,7 +705,7 @@ void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
g_BackgroundAudio.SFX().Play(UI::UISound::ACHIEVEMENT_UNLOCKED, achievementVolume);
});
- ChoiceGroup *uiSoundSettings = audioSettings->Add(new ChoiceGroup(a->T("UI sound")));
+ ChoiceGroup *uiSoundSettings = audioSettings->Add(new ChoiceGroup(""));
uiSoundSettings->Add(new CheckBox(&g_Config.bUISound, a->T("UI sound")));
PopupSliderChoice *uiVolume = uiSoundSettings->Add(new PopupSliderChoice(&g_Config.iUIVolume, 0, VOLUMEHI_FULL, Config::GetDefaultValueInt(&g_Config.iUIVolume), a->T("UI volume"), screenManager()));
diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp
index f556633c9d..230874aa7d 100644
--- a/UI/SavedataScreen.cpp
+++ b/UI/SavedataScreen.cpp
@@ -615,6 +615,7 @@ void SavedataScreen::CreateSavedataTab(UI::ViewGroup *savedata) {
sortOption_ = SavedataSortOption(e.a);
dataBrowser_->SetSortOption(sortOption_);
});
+ sortStrip->ApplyRoundCorners();
savedata->Add(sortStrip);
dataBrowser_ = savedata->Add(new SavedataBrowser(savedata_dir, new LayoutParams(FILL_PARENT, FILL_PARENT)));
@@ -638,6 +639,7 @@ void SavedataScreen::CreateSavestateTab(UI::ViewGroup *savestate) {
sortOption_ = SavedataSortOption(e.a);
stateBrowser_->SetSortOption(sortOption_);
});
+ sortStrip->ApplyRoundCorners();
savestate->Add(sortStrip);
stateBrowser_ = savestate->Add(new SavedataBrowser(savestate_dir));