Browse Source

Add helpers for creating/using Rectangles in UI contexts (#4734)

* Add MakeRectangle helper to convert from SDL_Rect

* Add Rectangle::inset method for shrinking a rectangle

Turns out some of the other use cases I though this could apply to were actually doing something based on a fixed region

* Simplify initialisation of settings menu rects
pull/4753/head
Andrew James 4 years ago committed by GitHub
parent
commit
13a3424ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Source/DiabloUI/button.cpp
  2. 17
      Source/DiabloUI/diabloui.cpp
  3. 4
      Source/DiabloUI/settingsmenu.cpp
  4. 13
      Source/engine/rectangle.hpp
  5. 40
      Source/panels/spell_book.cpp
  6. 4
      Source/utils/sdl_geometry.h

2
Source/DiabloUI/button.cpp

@ -18,7 +18,7 @@ void RenderButton(UiButton *button)
const Surface &out = Surface(DiabloUiSurface());
RenderPcxSprite(out, ButtonSprite(button->IsPressed()), { button->m_rect.x, button->m_rect.y });
Rectangle textRect { { button->m_rect.x, button->m_rect.y }, { button->m_rect.w, button->m_rect.h } };
Rectangle textRect = MakeRectangle(button->m_rect);
if (!button->IsPressed()) {
--textRect.position.y;
}

17
Source/DiabloUI/diabloui.cpp

@ -802,18 +802,14 @@ namespace {
void Render(const UiText *uiText)
{
Rectangle rect { { uiText->m_rect.x, uiText->m_rect.y }, { uiText->m_rect.w, uiText->m_rect.h } };
const Surface &out = Surface(DiabloUiSurface());
DrawString(out, uiText->GetText(), rect, uiText->GetFlags() | UiFlags::FontSizeDialog);
DrawString(out, uiText->GetText(), MakeRectangle(uiText->m_rect), uiText->GetFlags() | UiFlags::FontSizeDialog);
}
void Render(const UiArtText *uiArtText)
{
Rectangle rect { { uiArtText->m_rect.x, uiArtText->m_rect.y }, { uiArtText->m_rect.w, uiArtText->m_rect.h } };
const Surface &out = Surface(DiabloUiSurface());
DrawString(out, uiArtText->GetText(), rect, uiArtText->GetFlags(), uiArtText->GetSpacing(), uiArtText->GetLineHeight());
DrawString(out, uiArtText->GetText(), MakeRectangle(uiArtText->m_rect), uiArtText->GetFlags(), uiArtText->GetSpacing(), uiArtText->GetLineHeight());
}
void Render(const UiImage *uiImage)
@ -865,10 +861,8 @@ void Render(const UiImageAnimatedPcx *uiImage)
void Render(const UiArtTextButton *uiButton)
{
Rectangle rect { { uiButton->m_rect.x, uiButton->m_rect.y }, { uiButton->m_rect.w, uiButton->m_rect.h } };
const Surface &out = Surface(DiabloUiSurface());
DrawString(out, uiButton->GetText(), rect, uiButton->GetFlags());
DrawString(out, uiButton->GetText(), MakeRectangle(uiButton->m_rect), uiButton->GetFlags());
}
void Render(const UiList *uiList)
@ -881,7 +875,7 @@ void Render(const UiList *uiList)
if (i == SelectedItem)
DrawSelector(rect);
Rectangle rectangle { { rect.x, rect.y }, { rect.w, rect.h } };
Rectangle rectangle = MakeRectangle(rect);
if (item->args.empty())
DrawString(out, item->m_text, rectangle, uiList->GetFlags() | item->uiFlags, uiList->GetSpacing());
else
@ -928,7 +922,8 @@ void Render(const UiEdit *uiEdit)
{
DrawSelector(uiEdit->m_rect);
Rectangle rect { { uiEdit->m_rect.x + 43, uiEdit->m_rect.y + 1 }, { uiEdit->m_rect.w - 86, uiEdit->m_rect.h } };
// To simulate padding we inset the region used to draw text in an edit control
Rectangle rect = MakeRectangle(uiEdit->m_rect).inset({ 43, 1 });
const Surface &out = Surface(DiabloUiSurface());
DrawString(out, uiEdit->m_value, rect, uiEdit->GetFlags() | UiFlags::TextCursor);

4
Source/DiabloUI/settingsmenu.cpp

@ -254,8 +254,8 @@ void UiSettingsMenu()
const Rectangle &uiRectangle = GetUIRectangle();
rectList = { { uiRectangle.position.x + 50, (uiRectangle.position.y + 204) }, { 540, 208 } };
rectDescription = { { uiRectangle.position.x + 24, rectList.position.y + rectList.size.height + 16 }, { 590, 35 } };
rectList = { uiRectangle.position + Displacement { 50, 204 }, Size { 540, 208 } };
rectDescription = { rectList.position + Displacement { -26, rectList.size.height + 16 }, Size { 590, 35 } };
optionDescription[0] = '\0';

13
Source/engine/rectangle.hpp

@ -46,6 +46,19 @@ struct Rectangle {
{
return position + Displacement(size / 2);
}
/**
* @brief Returns a rectangle with all sides shrunk according to the given displacement
*
* Effectively moves the left/right edges in by deltaX, and the top/bottom edges in by deltaY
*/
constexpr Rectangle inset(Displacement factor) const
{
return {
position + factor,
Size { size.width - factor.deltaX * 2, size.height - factor.deltaY * 2 }
};
}
};
} // namespace devilution

40
Source/panels/spell_book.cpp

@ -38,16 +38,15 @@ spell_id SpellPages[6][7] = {
{ SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID }
};
constexpr int SpellBookDescriptionWidth = 250;
constexpr int SpellBookDescriptionHeight = 43;
constexpr int SpellBookDescriptionPaddingLeft = 2;
constexpr int SpellBookDescriptionPaddingRight = 2;
constexpr Size SpellBookDescription { 250, 43 };
constexpr int SpellBookDescriptionPaddingHorizontal = 2;
void PrintSBookStr(const Surface &out, Point position, string_view text, UiFlags flags = UiFlags::None)
{
DrawString(out, text,
{ GetPanelPosition(UiPanels::Spell, { SPLICONLENGTH + SpellBookDescriptionPaddingLeft + position.x, position.y }),
{ SpellBookDescriptionWidth - SpellBookDescriptionPaddingLeft - SpellBookDescriptionPaddingRight, 0 } },
Rectangle(GetPanelPosition(UiPanels::Spell, position + Displacement { SPLICONLENGTH, 0 }),
SpellBookDescription)
.inset({ SpellBookDescriptionPaddingHorizontal, 0 }),
UiFlags::ColorWhite | flags);
}
@ -140,7 +139,7 @@ void DrawSpellBook(const Surface &out)
if (sn != SPL_INVALID && (spl & GetSpellBitmask(sn)) != 0) {
spell_type st = GetSBookTrans(sn, true);
SetSpellTrans(st);
const Point spellCellPosition = GetPanelPosition(UiPanels::Spell, { 11, yp + SpellBookDescriptionHeight });
const Point spellCellPosition = GetPanelPosition(UiPanels::Spell, { 11, yp + SpellBookDescription.height });
DrawSpellCel(out, spellCellPosition, *pSBkIconCels, SpellITbl[sn]);
if (sn == player._pRSpell && st == player._pRSplType) {
SetSpellTrans(RSPLTYPE_SKILL);
@ -184,16 +183,19 @@ void DrawSpellBook(const Surface &out)
} break;
}
}
yp += SpellBookDescriptionHeight;
yp += SpellBookDescription.height;
}
}
void CheckSBook()
{
Rectangle iconArea = { GetPanelPosition(UiPanels::Spell, { 11, 18 }), { 48 - 11, 314 - 18 } };
Rectangle tabArea = { GetPanelPosition(UiPanels::Spell, { 7, 320 }), { 311 - 7, 349 - 320 } };
// Icons are drawn in a column near the left side of the panel and aligned with the spell book description entries
// Spell icons/buttons are 37x38 pixels, laid out from 11,18 with a 5 pixel margin between each icon. This is close
// enough to the height of the space given to spell descriptions that we can reuse that value and subtract the
// padding from the end of the area.
Rectangle iconArea = { GetPanelPosition(UiPanels::Spell, { 11, 18 }), Size { 37, SpellBookDescription.height * 7 - 5 } };
if (iconArea.Contains(MousePosition)) {
spell_id sn = SpellPages[sbooktab][(MousePosition.y - GetRightPanel().position.y - 18) / 43];
spell_id sn = SpellPages[sbooktab][(MousePosition.y - iconArea.position.y) / SpellBookDescription.height];
Player &player = *MyPlayer;
uint64_t spl = player._pMemSpells | player._pISpells | player._pAblSpells;
if (sn != SPL_INVALID && (spl & GetSpellBitmask(sn)) != 0) {
@ -208,9 +210,23 @@ void CheckSBook()
player._pRSplType = st;
force_redraw = 255;
}
return;
}
// The width of the panel excluding the border is 305 pixels. This does not cleanly divide by 4 meaning Diablo tabs
// end up with an extra pixel somewhere around the buttons. Vanilla Diablo had the buttons left-aligned, devilutionX
// instead justifies the buttons and puts the gap between buttons 2/3. See DrawSpellBook
const int TabWidth = gbIsHellfire ? 61 : 76;
// Tabs are drawn in a row near the bottom of the panel
Rectangle tabArea = { GetPanelPosition(UiPanels::Spell, { 7, 320 }), Size { 305, 29 } };
if (tabArea.Contains(MousePosition)) {
sbooktab = (MousePosition.x - (GetRightPanel().position.x + 7)) / (gbIsHellfire ? 61 : 76);
int hitColumn = MousePosition.x - tabArea.position.x;
// Clicking on the gutter currently activates tab 3. Could make it do nothing by checking for == here and return early.
if (!gbIsHellfire && hitColumn > TabWidth * 2) {
// Subtract 1 pixel to account for the gutter between buttons 2/3
hitColumn--;
}
sbooktab = hitColumn / TabWidth;
}
}

4
Source/utils/sdl_geometry.h

@ -31,4 +31,8 @@ inline SDL_Rect MakeSdlRect(Rectangle rect)
return MakeSdlRect(rect.position.x, rect.position.y, rect.size.width, rect.size.height);
}
constexpr Rectangle MakeRectangle(SDL_Rect sdlRect)
{
return { Point { sdlRect.x, sdlRect.y }, Size { sdlRect.w, sdlRect.h } };
}
} // namespace devilution

Loading…
Cancel
Save