| [27763] | 1 | /* Copyright (C) 2023 Wildfire Games.
|
|---|
| [27965] | 2 | * This file is part of 0 A.D.
|
|---|
| [6830] | 3 | *
|
|---|
| [27965] | 4 | * 0 A.D. is free software: you can redistribute it and/or modify
|
|---|
| [6830] | 5 | * it under the terms of the GNU General Public License as published by
|
|---|
| 6 | * the Free Software Foundation, either version 2 of the License, or
|
|---|
| 7 | * (at your option) any later version.
|
|---|
| 8 | *
|
|---|
| [27965] | 9 | * 0 A.D. is distributed in the hope that it will be useful,
|
|---|
| [6830] | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 12 | * GNU General Public License for more details.
|
|---|
| 13 | *
|
|---|
| 14 | * You should have received a copy of the GNU General Public License
|
|---|
| [27965] | 15 | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|---|
| [6830] | 16 | */
|
|---|
| 17 |
|
|---|
| [3802] | 18 | #include "precompiled.h"
|
|---|
| [13521] | 19 |
|
|---|
| [3802] | 20 | #include "CDropDown.h"
|
|---|
| 21 |
|
|---|
| [22931] | 22 | #include "gui/CGUI.h"
|
|---|
| [22976] | 23 | #include "gui/IGUIScrollBar.h"
|
|---|
| [23028] | 24 | #include "gui/SettingTypes/CGUIColor.h"
|
|---|
| 25 | #include "gui/SettingTypes/CGUIList.h"
|
|---|
| [13521] | 26 | #include "lib/external_libraries/libsdl.h"
|
|---|
| [13556] | 27 | #include "lib/timer.h"
|
|---|
| [22863] | 28 | #include "ps/Profile.h"
|
|---|
| [3802] | 29 |
|
|---|
| [22741] | 30 | CDropDown::CDropDown(CGUI& pGUI)
|
|---|
| [23005] | 31 | : CList(pGUI),
|
|---|
| 32 | m_Open(),
|
|---|
| 33 | m_HideScrollBar(),
|
|---|
| 34 | m_ElementHighlight(-1),
|
|---|
| [25392] | 35 | m_ButtonWidth(this, "button_width"),
|
|---|
| 36 | m_DropDownSize(this, "dropdown_size"),
|
|---|
| 37 | m_DropDownBuffer(this, "dropdown_buffer"),
|
|---|
| 38 | m_MinimumVisibleItems(this, "minimum_visible_items"),
|
|---|
| 39 | m_SoundClosed(this, "sound_closed"),
|
|---|
| 40 | m_SoundEnter(this, "sound_enter"),
|
|---|
| 41 | m_SoundLeave(this, "sound_leave"),
|
|---|
| 42 | m_SoundOpened(this, "sound_opened"),
|
|---|
| 43 | // Setting "sprite" is registered by CList and used as the background
|
|---|
| 44 | m_SpriteDisabled(this, "sprite_disabled"),
|
|---|
| [25587] | 45 | m_SpriteOverlayDisabled(this, "sprite_overlay_disabled"),
|
|---|
| [25392] | 46 | m_SpriteList(this, "sprite_list"), // Background of the drop down list
|
|---|
| [25587] | 47 | m_SpriteListOverlay(this, "sprite_list_overlay"), // Overlay above the drop down list
|
|---|
| [25392] | 48 | m_Sprite2(this, "sprite2"), // Button that sits to the right
|
|---|
| 49 | m_Sprite2Over(this, "sprite2_over"),
|
|---|
| 50 | m_Sprite2Pressed(this, "sprite2_pressed"),
|
|---|
| 51 | m_Sprite2Disabled(this, "sprite2_disabled"),
|
|---|
| 52 | m_TextColorDisabled(this, "textcolor_disabled")
|
|---|
| 53 | // Add these in CList! And implement TODO
|
|---|
| [27763] | 54 | //m_TextColorOver("textcolor_over");
|
|---|
| 55 | //m_TextColorPressed("textcolor_pressed");
|
|---|
| [3802] | 56 | {
|
|---|
| [25392] | 57 | m_ScrollBar.Set(true, true);
|
|---|
| [3802] | 58 | }
|
|---|
| 59 |
|
|---|
| 60 | CDropDown::~CDropDown()
|
|---|
| 61 | {
|
|---|
| 62 | }
|
|---|
| 63 |
|
|---|
| 64 | void CDropDown::SetupText()
|
|---|
| 65 | {
|
|---|
| [7649] | 66 | SetupListRect();
|
|---|
| [3802] | 67 | CList::SetupText();
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| [19845] | 70 | void CDropDown::UpdateCachedSize()
|
|---|
| 71 | {
|
|---|
| 72 | CList::UpdateCachedSize();
|
|---|
| 73 | SetupText();
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| [16931] | 76 | void CDropDown::HandleMessage(SGUIMessage& Message)
|
|---|
| [3802] | 77 | {
|
|---|
| [23020] | 78 | // CList::HandleMessage(Message); placed after the switch!
|
|---|
| [3802] | 79 |
|
|---|
| 80 | switch (Message.type)
|
|---|
| 81 | {
|
|---|
| 82 | case GUIM_SETTINGS_UPDATED:
|
|---|
| [7649] | 83 | {
|
|---|
| [3802] | 84 | // Update cached list rect
|
|---|
| [7649] | 85 | if (Message.value == "size" ||
|
|---|
| 86 | Message.value == "absolute" ||
|
|---|
| 87 | Message.value == "dropdown_size" ||
|
|---|
| 88 | Message.value == "dropdown_buffer" ||
|
|---|
| [21379] | 89 | Message.value == "minimum_visible_items" ||
|
|---|
| [7649] | 90 | Message.value == "scrollbar_style" ||
|
|---|
| 91 | Message.value == "button_width")
|
|---|
| [3802] | 92 | {
|
|---|
| 93 | SetupListRect();
|
|---|
| [16931] | 94 | }
|
|---|
| [3802] | 95 |
|
|---|
| 96 | break;
|
|---|
| [7649] | 97 | }
|
|---|
| [3802] | 98 |
|
|---|
| [7649] | 99 | case GUIM_MOUSE_MOTION:
|
|---|
| 100 | {
|
|---|
| [16931] | 101 | if (!m_Open)
|
|---|
| 102 | break;
|
|---|
| 103 |
|
|---|
| [25152] | 104 | CVector2D mouse = m_pGUI.GetMousePos();
|
|---|
| [16931] | 105 |
|
|---|
| 106 | if (!GetListRect().PointInside(mouse))
|
|---|
| 107 | break;
|
|---|
| 108 |
|
|---|
| [23005] | 109 | const float scroll = m_ScrollBar ? GetScrollBar(0).GetPos() : 0.f;
|
|---|
| [16931] | 110 |
|
|---|
| 111 | CRect rect = GetListRect();
|
|---|
| [25152] | 112 | mouse.Y += scroll;
|
|---|
| [16931] | 113 | int set = -1;
|
|---|
| [25392] | 114 | for (int i = 0; i < static_cast<int>(m_List->m_Items.size()); ++i)
|
|---|
| [3802] | 115 | {
|
|---|
| [25152] | 116 | if (mouse.Y >= rect.top + m_ItemsYPositions[i] &&
|
|---|
| 117 | mouse.Y < rect.top + m_ItemsYPositions[i+1] &&
|
|---|
| [16931] | 118 | // mouse is not over scroll-bar
|
|---|
| [17149] | 119 | (m_HideScrollBar ||
|
|---|
| [25152] | 120 | mouse.X < GetScrollBar(0).GetOuterRect().left ||
|
|---|
| 121 | mouse.X > GetScrollBar(0).GetOuterRect().right))
|
|---|
| [3802] | 122 | {
|
|---|
| [16931] | 123 | set = i;
|
|---|
| [3802] | 124 | }
|
|---|
| 125 | }
|
|---|
| 126 |
|
|---|
| [16931] | 127 | if (set != -1)
|
|---|
| 128 | {
|
|---|
| 129 | m_ElementHighlight = set;
|
|---|
| 130 | //UpdateAutoScroll();
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| [7649] | 133 | break;
|
|---|
| 134 | }
|
|---|
| [3802] | 135 |
|
|---|
| [13521] | 136 | case GUIM_MOUSE_ENTER:
|
|---|
| 137 | {
|
|---|
| [23005] | 138 | if (m_Enabled)
|
|---|
| 139 | PlaySound(m_SoundEnter);
|
|---|
| [13521] | 140 | break;
|
|---|
| 141 | }
|
|---|
| 142 |
|
|---|
| [7649] | 143 | case GUIM_MOUSE_LEAVE:
|
|---|
| 144 | {
|
|---|
| [23005] | 145 | m_ElementHighlight = m_Selected;
|
|---|
| [3802] | 146 |
|
|---|
| [23005] | 147 | if (m_Enabled)
|
|---|
| 148 | PlaySound(m_SoundLeave);
|
|---|
| [7649] | 149 | break;
|
|---|
| 150 | }
|
|---|
| [3802] | 151 |
|
|---|
| [7649] | 152 | // We can't inherent this routine from CList, because we need to include
|
|---|
| 153 | // a mouse click to open the dropdown, also the coordinates are changed.
|
|---|
| [3802] | 154 | case GUIM_MOUSE_PRESS_LEFT:
|
|---|
| 155 | {
|
|---|
| [23005] | 156 | if (!m_Enabled)
|
|---|
| [13521] | 157 | {
|
|---|
| [23005] | 158 | PlaySound(m_SoundDisabled);
|
|---|
| [7649] | 159 | break;
|
|---|
| [13521] | 160 | }
|
|---|
| [7649] | 161 |
|
|---|
| [3802] | 162 | if (!m_Open)
|
|---|
| 163 | {
|
|---|
| [25392] | 164 | if (m_List->m_Items.empty())
|
|---|
| [13936] | 165 | return;
|
|---|
| 166 |
|
|---|
| [3802] | 167 | m_Open = true;
|
|---|
| [7649] | 168 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| [23005] | 169 | m_ElementHighlight = m_Selected;
|
|---|
| [12319] | 170 |
|
|---|
| 171 | // Start at the position of the selected item, if possible.
|
|---|
| [25830] | 172 | if (m_ItemsYPositions.empty() || m_ElementHighlight < 0 || static_cast<size_t>(m_ElementHighlight) >= m_ItemsYPositions.size())
|
|---|
| 173 | GetScrollBar(0).SetPos(0);
|
|---|
| 174 | else
|
|---|
| 175 | GetScrollBar(0).SetPos(m_ItemsYPositions[m_ElementHighlight] - 60);
|
|---|
| [13521] | 176 |
|
|---|
| [23005] | 177 | PlaySound(m_SoundOpened);
|
|---|
| [3802] | 178 | return; // overshadow
|
|---|
| 179 | }
|
|---|
| 180 | else
|
|---|
| 181 | {
|
|---|
| [25152] | 182 | const CVector2D& mouse = m_pGUI.GetMousePos();
|
|---|
| [3802] | 183 |
|
|---|
| 184 | // If the regular area is pressed, then abort, and close.
|
|---|
| 185 | if (m_CachedActualSize.PointInside(mouse))
|
|---|
| 186 | {
|
|---|
| 187 | m_Open = false;
|
|---|
| [7649] | 188 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| [23005] | 189 | PlaySound(m_SoundClosed);
|
|---|
| [3802] | 190 | return; // overshadow
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| [17149] | 193 | if (m_HideScrollBar ||
|
|---|
| [25152] | 194 | mouse.X < GetScrollBar(0).GetOuterRect().left ||
|
|---|
| 195 | mouse.X > GetScrollBar(0).GetOuterRect().right ||
|
|---|
| 196 | mouse.Y < GetListRect().top)
|
|---|
| [3802] | 197 | {
|
|---|
| 198 | m_Open = false;
|
|---|
| [7649] | 199 | GetScrollBar(0).SetZ(GetBufferedZ());
|
|---|
| [3802] | 200 | }
|
|---|
| 201 | }
|
|---|
| [7649] | 202 | break;
|
|---|
| 203 | }
|
|---|
| [3802] | 204 |
|
|---|
| [14458] | 205 | case GUIM_MOUSE_WHEEL_DOWN:
|
|---|
| 206 | {
|
|---|
| [14460] | 207 | // Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
|
|---|
| [23005] | 208 | if (m_Open || !m_Enabled)
|
|---|
| [14458] | 209 | break;
|
|---|
| 210 |
|
|---|
| [23005] | 211 | m_ElementHighlight = m_Selected;
|
|---|
| [22765] | 212 |
|
|---|
| [14458] | 213 | if (m_ElementHighlight + 1 >= (int)m_ItemsYPositions.size() - 1)
|
|---|
| 214 | break;
|
|---|
| 215 |
|
|---|
| [16931] | 216 | ++m_ElementHighlight;
|
|---|
| [25392] | 217 | m_Selected.Set(m_ElementHighlight, true);
|
|---|
| [14458] | 218 | break;
|
|---|
| 219 | }
|
|---|
| 220 |
|
|---|
| 221 | case GUIM_MOUSE_WHEEL_UP:
|
|---|
| 222 | {
|
|---|
| 223 | // Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
|
|---|
| [23005] | 224 | if (m_Open || !m_Enabled)
|
|---|
| [14458] | 225 | break;
|
|---|
| 226 |
|
|---|
| [23005] | 227 | m_ElementHighlight = m_Selected;
|
|---|
| [14458] | 228 | if (m_ElementHighlight - 1 < 0)
|
|---|
| 229 | break;
|
|---|
| 230 |
|
|---|
| [22796] | 231 | --m_ElementHighlight;
|
|---|
| [25392] | 232 | m_Selected.Set(m_ElementHighlight, true);
|
|---|
| [14458] | 233 | break;
|
|---|
| 234 | }
|
|---|
| 235 |
|
|---|
| [3802] | 236 | case GUIM_LOST_FOCUS:
|
|---|
| [13521] | 237 | {
|
|---|
| 238 | if (m_Open)
|
|---|
| [23005] | 239 | PlaySound(m_SoundClosed);
|
|---|
| [22756] | 240 |
|
|---|
| [7649] | 241 | m_Open = false;
|
|---|
| [3802] | 242 | break;
|
|---|
| [13521] | 243 | }
|
|---|
| [3802] | 244 |
|
|---|
| 245 | case GUIM_LOAD:
|
|---|
| 246 | SetupListRect();
|
|---|
| 247 | break;
|
|---|
| 248 |
|
|---|
| 249 | default:
|
|---|
| 250 | break;
|
|---|
| 251 | }
|
|---|
| 252 |
|
|---|
| 253 | // Important that this is after, so that overshadowed implementations aren't processed
|
|---|
| 254 | CList::HandleMessage(Message);
|
|---|
| [12244] | 255 |
|
|---|
| 256 | // As HandleMessage functions return void, we need to manually verify
|
|---|
| 257 | // whether the child list's items were modified.
|
|---|
| 258 | if (CList::GetModified())
|
|---|
| 259 | SetupText();
|
|---|
| [3802] | 260 | }
|
|---|
| 261 |
|
|---|
| [24215] | 262 | InReaction CDropDown::ManuallyHandleKeys(const SDL_Event_* ev)
|
|---|
| [3802] | 263 | {
|
|---|
| [15909] | 264 | InReaction result = IN_PASS;
|
|---|
| [3802] | 265 | bool update_highlight = false;
|
|---|
| 266 |
|
|---|
| [15909] | 267 | if (ev->ev.type == SDL_KEYDOWN)
|
|---|
| [3802] | 268 | {
|
|---|
| [15909] | 269 | int szChar = ev->ev.key.keysym.sym;
|
|---|
| [16931] | 270 |
|
|---|
| [15909] | 271 | switch (szChar)
|
|---|
| 272 | {
|
|---|
| 273 | case '\r':
|
|---|
| 274 | m_Open = false;
|
|---|
| 275 | result = IN_HANDLED;
|
|---|
| 276 | break;
|
|---|
| [3802] | 277 |
|
|---|
| [15909] | 278 | case SDLK_HOME:
|
|---|
| 279 | case SDLK_END:
|
|---|
| 280 | case SDLK_UP:
|
|---|
| 281 | case SDLK_DOWN:
|
|---|
| 282 | case SDLK_PAGEUP:
|
|---|
| 283 | case SDLK_PAGEDOWN:
|
|---|
| 284 | if (!m_Open)
|
|---|
| 285 | return IN_PASS;
|
|---|
| 286 | // Set current selected item to highlighted, before
|
|---|
| [24215] | 287 | // then really processing these in CList::ManuallyHandleKeys()
|
|---|
| [25392] | 288 | m_Selected.Set(m_ElementHighlight, true);
|
|---|
| [15909] | 289 | update_highlight = true;
|
|---|
| 290 | break;
|
|---|
| [3802] | 291 |
|
|---|
| [15909] | 292 | default:
|
|---|
| [24488] | 293 | // If we have typed a character try to get the closest element to it.
|
|---|
| [15909] | 294 | // TODO: not too nice and doesn't deal with dashes.
|
|---|
| 295 | if (m_Open && ((szChar >= SDLK_a && szChar <= SDLK_z) || szChar == SDLK_SPACE
|
|---|
| 296 | || (szChar >= SDLK_0 && szChar <= SDLK_9)
|
|---|
| [24488] | 297 | || (szChar >= SDLK_KP_1 && szChar <= SDLK_KP_0)))
|
|---|
| [15909] | 298 | {
|
|---|
| 299 | // arbitrary 1 second limit to add to string or start fresh.
|
|---|
| 300 | // maximal amount of characters is 100, which imo is far more than enough.
|
|---|
| 301 | if (timer_Time() - m_TimeOfLastInput > 1.0 || m_InputBuffer.length() >= 100)
|
|---|
| 302 | m_InputBuffer = szChar;
|
|---|
| 303 | else
|
|---|
| 304 | m_InputBuffer += szChar;
|
|---|
| [16931] | 305 |
|
|---|
| [15909] | 306 | m_TimeOfLastInput = timer_Time();
|
|---|
| [16931] | 307 |
|
|---|
| [15909] | 308 | // let's look for the closest element
|
|---|
| 309 | // basically it's alphabetic order and "as many letters as we can get".
|
|---|
| 310 | int closest = -1;
|
|---|
| 311 | int bestIndex = -1;
|
|---|
| 312 | int difference = 1250;
|
|---|
| [25392] | 313 | for (int i = 0; i < static_cast<int>(m_List->m_Items.size()); ++i)
|
|---|
| [13556] | 314 | {
|
|---|
| [15909] | 315 | int indexOfDifference = 0;
|
|---|
| 316 | int diff = 0;
|
|---|
| [16931] | 317 | for (size_t j = 0; j < m_InputBuffer.length(); ++j)
|
|---|
| [15909] | 318 | {
|
|---|
| [25392] | 319 | diff = std::abs(static_cast<int>(m_List->m_Items[i].GetRawString().LowerCase()[j]) - static_cast<int>(m_InputBuffer[j]));
|
|---|
| [15909] | 320 | if (diff == 0)
|
|---|
| 321 | indexOfDifference = j+1;
|
|---|
| 322 | else
|
|---|
| 323 | break;
|
|---|
| 324 | }
|
|---|
| 325 | if (indexOfDifference > bestIndex || (indexOfDifference >= bestIndex && diff < difference))
|
|---|
| 326 | {
|
|---|
| 327 | bestIndex = indexOfDifference;
|
|---|
| 328 | closest = i;
|
|---|
| 329 | difference = diff;
|
|---|
| 330 | }
|
|---|
| [13556] | 331 | }
|
|---|
| [15909] | 332 | // let's select the closest element. There should basically always be one.
|
|---|
| 333 | if (closest != -1)
|
|---|
| [13556] | 334 | {
|
|---|
| [25392] | 335 | m_Selected.Set(closest, true);
|
|---|
| [15909] | 336 | update_highlight = true;
|
|---|
| 337 | GetScrollBar(0).SetPos(m_ItemsYPositions[closest] - 60);
|
|---|
| [13556] | 338 | }
|
|---|
| [15909] | 339 | result = IN_HANDLED;
|
|---|
| [13556] | 340 | }
|
|---|
| [15909] | 341 | break;
|
|---|
| [13556] | 342 | }
|
|---|
| [3802] | 343 | }
|
|---|
| 344 |
|
|---|
| [24215] | 345 | if (CList::ManuallyHandleKeys(ev) == IN_HANDLED)
|
|---|
| [15909] | 346 | result = IN_HANDLED;
|
|---|
| [3802] | 347 |
|
|---|
| 348 | if (update_highlight)
|
|---|
| [23005] | 349 | m_ElementHighlight = m_Selected;
|
|---|
| [3802] | 350 |
|
|---|
| [15909] | 351 | return result;
|
|---|
| [3802] | 352 | }
|
|---|
| 353 |
|
|---|
| 354 | void CDropDown::SetupListRect()
|
|---|
| 355 | {
|
|---|
| [25577] | 356 | const CSize2D windowSize = m_pGUI.GetWindowSize();
|
|---|
| [3802] | 357 |
|
|---|
| [21379] | 358 | if (m_ItemsYPositions.empty())
|
|---|
| [3802] | 359 | {
|
|---|
| [23005] | 360 | m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer,
|
|---|
| 361 | m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize);
|
|---|
| [21379] | 362 | m_HideScrollBar = false;
|
|---|
| 363 | }
|
|---|
| 364 | // Too many items so use a scrollbar
|
|---|
| [23005] | 365 | else if (m_ItemsYPositions.back() > m_DropDownSize)
|
|---|
| [21379] | 366 | {
|
|---|
| 367 | // Place items below if at least some items can be placed below
|
|---|
| [25577] | 368 | if (m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize <= windowSize.Height)
|
|---|
| [23005] | 369 | m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer,
|
|---|
| 370 | m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize);
|
|---|
| [25577] | 371 | else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - m_CachedActualSize.bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) ||
|
|---|
| 372 | m_CachedActualSize.top < windowSize.Height - m_CachedActualSize.bottom)
|
|---|
| [23005] | 373 | m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer,
|
|---|
| [25577] | 374 | m_CachedActualSize.right, windowSize.Height);
|
|---|
| [21379] | 375 | // Not enough space below, thus place items above
|
|---|
| 376 | else
|
|---|
| [23005] | 377 | m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - m_DropDownBuffer - m_DropDownSize),
|
|---|
| 378 | m_CachedActualSize.right, m_CachedActualSize.top - m_DropDownBuffer);
|
|---|
| [3802] | 379 |
|
|---|
| 380 | m_HideScrollBar = false;
|
|---|
| 381 | }
|
|---|
| 382 | else
|
|---|
| 383 | {
|
|---|
| [21379] | 384 | // Enough space below, no scrollbar needed
|
|---|
| [25577] | 385 | if (m_CachedActualSize.bottom + m_DropDownBuffer + m_ItemsYPositions.back() <= windowSize.Height)
|
|---|
| [21379] | 386 | {
|
|---|
| [23005] | 387 | m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer,
|
|---|
| 388 | m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_ItemsYPositions.back());
|
|---|
| [21379] | 389 | m_HideScrollBar = true;
|
|---|
| 390 | }
|
|---|
| 391 | // Enough space below for some items, but not all, so place items below and use a scrollbar
|
|---|
| [25577] | 392 | else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - m_CachedActualSize.bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) ||
|
|---|
| 393 | m_CachedActualSize.top < windowSize.Height - m_CachedActualSize.bottom)
|
|---|
| [21379] | 394 | {
|
|---|
| [23005] | 395 | m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer,
|
|---|
| [25577] | 396 | m_CachedActualSize.right, windowSize.Height);
|
|---|
| [21379] | 397 | m_HideScrollBar = false;
|
|---|
| 398 | }
|
|---|
| 399 | // Not enough space below, thus place items above. Hide the scrollbar accordingly
|
|---|
| 400 | else
|
|---|
| 401 | {
|
|---|
| [23005] | 402 | m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - m_DropDownBuffer - m_ItemsYPositions.back()),
|
|---|
| 403 | m_CachedActualSize.right, m_CachedActualSize.top - m_DropDownBuffer);
|
|---|
| 404 | m_HideScrollBar = m_CachedActualSize.top > m_ItemsYPositions.back() + m_DropDownBuffer;
|
|---|
| [21379] | 405 | }
|
|---|
| [3802] | 406 | }
|
|---|
| 407 | }
|
|---|
| 408 |
|
|---|
| 409 | CRect CDropDown::GetListRect() const
|
|---|
| 410 | {
|
|---|
| 411 | return m_CachedListRect;
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|
| [22757] | 414 | bool CDropDown::IsMouseOver() const
|
|---|
| [3802] | 415 | {
|
|---|
| 416 | if (m_Open)
|
|---|
| 417 | {
|
|---|
| [21379] | 418 | CRect rect(m_CachedActualSize.left, std::min(m_CachedActualSize.top, GetListRect().top),
|
|---|
| 419 | m_CachedActualSize.right, std::max(m_CachedActualSize.bottom, GetListRect().bottom));
|
|---|
| [22741] | 420 | return rect.PointInside(m_pGUI.GetMousePos());
|
|---|
| [3802] | 421 | }
|
|---|
| 422 | else
|
|---|
| [22741] | 423 | return m_CachedActualSize.PointInside(m_pGUI.GetMousePos());
|
|---|
| [3802] | 424 | }
|
|---|
| 425 |
|
|---|
| [25591] | 426 | void CDropDown::Draw(CCanvas2D& canvas)
|
|---|
| [3802] | 427 | {
|
|---|
| [23005] | 428 | const CGUISpriteInstance& sprite = m_Enabled ? m_Sprite : m_SpriteDisabled;
|
|---|
| [25587] | 429 | const CGUISpriteInstance& spriteOverlay = m_Enabled ? m_SpriteOverlay : m_SpriteOverlayDisabled;
|
|---|
| [22693] | 430 |
|
|---|
| [25591] | 431 | m_pGUI.DrawSprite(sprite, canvas, m_CachedActualSize);
|
|---|
| [3802] | 432 |
|
|---|
| [23005] | 433 | if (m_ButtonWidth > 0.f)
|
|---|
| [3802] | 434 | {
|
|---|
| [23005] | 435 | CRect rect(m_CachedActualSize.right - m_ButtonWidth, m_CachedActualSize.top,
|
|---|
| [3802] | 436 | m_CachedActualSize.right, m_CachedActualSize.bottom);
|
|---|
| 437 |
|
|---|
| [23005] | 438 | if (!m_Enabled)
|
|---|
| [3802] | 439 | {
|
|---|
| [25591] | 440 | m_pGUI.DrawSprite(*m_Sprite2Disabled ? m_Sprite2Disabled : m_Sprite2, canvas, rect);
|
|---|
| [3802] | 441 | }
|
|---|
| [16931] | 442 | else if (m_Open)
|
|---|
| [3802] | 443 | {
|
|---|
| [25591] | 444 | m_pGUI.DrawSprite(*m_Sprite2Pressed ? m_Sprite2Pressed : m_Sprite2, canvas, rect);
|
|---|
| [3802] | 445 | }
|
|---|
| [16931] | 446 | else if (m_MouseHovering)
|
|---|
| [3802] | 447 | {
|
|---|
| [25591] | 448 | m_pGUI.DrawSprite(*m_Sprite2Over ? m_Sprite2Over : m_Sprite2, canvas, rect);
|
|---|
| [3802] | 449 | }
|
|---|
| [16931] | 450 | else
|
|---|
| [25591] | 451 | m_pGUI.DrawSprite(m_Sprite2, canvas, rect);
|
|---|
| [3802] | 452 | }
|
|---|
| 453 |
|
|---|
| [23005] | 454 | if (m_Selected != -1) // TODO: Maybe check validity completely?
|
|---|
| [3802] | 455 | {
|
|---|
| 456 | CRect cliparea(m_CachedActualSize.left, m_CachedActualSize.top,
|
|---|
| [23005] | 457 | m_CachedActualSize.right - m_ButtonWidth, m_CachedActualSize.bottom);
|
|---|
| [3802] | 458 |
|
|---|
| [25152] | 459 | CVector2D pos(m_CachedActualSize.left, m_CachedActualSize.top);
|
|---|
| [25591] | 460 | DrawText(canvas, m_Selected, m_Enabled ? m_TextColorSelected : m_TextColorDisabled, pos, cliparea);
|
|---|
| [3802] | 461 | }
|
|---|
| 462 |
|
|---|
| 463 | if (m_Open)
|
|---|
| 464 | {
|
|---|
| [25303] | 465 | // Disable scrollbar during drawing without sending a setting-changed message
|
|---|
| 466 | const bool old = m_ScrollBar;
|
|---|
| 467 |
|
|---|
| [23005] | 468 | // TODO: drawScrollbar as an argument of DrawList?
|
|---|
| [3802] | 469 | if (m_HideScrollBar)
|
|---|
| [25392] | 470 | m_ScrollBar.Set(false, false);
|
|---|
| [3802] | 471 |
|
|---|
| [25591] | 472 | DrawList(canvas, m_ElementHighlight, m_SpriteList, m_SpriteListOverlay, m_SpriteSelectArea, m_SpriteSelectAreaOverlay, m_TextColor);
|
|---|
| [16931] | 473 |
|
|---|
| [3802] | 474 | if (m_HideScrollBar)
|
|---|
| [25392] | 475 | m_ScrollBar.Set(old, false);
|
|---|
| [3802] | 476 | }
|
|---|
| [25591] | 477 | m_pGUI.DrawSprite(spriteOverlay, canvas, m_CachedActualSize);
|
|---|
| [3802] | 478 | }
|
|---|
| 479 |
|
|---|
| 480 | // When a dropdown list is opened, it needs to be visible above all the other
|
|---|
| 481 | // controls on the page. The only way I can think of to do this is to increase
|
|---|
| 482 | // its z value when opened, so that it's probably on top.
|
|---|
| 483 | float CDropDown::GetBufferedZ() const
|
|---|
| 484 | {
|
|---|
| 485 | float bz = CList::GetBufferedZ();
|
|---|
| 486 | if (m_Open)
|
|---|
| [6226] | 487 | return std::min(bz + 500.f, 1000.f); // TODO - don't use magic number for max z value
|
|---|
| [3802] | 488 | else
|
|---|
| 489 | return bz;
|
|---|
| 490 | }
|
|---|