Refactor ObjectLoadError to class (#15573)

* Move into class

* Refactor names and const

* Apply review comments
This commit is contained in:
Duncan 2021-10-14 16:53:48 +01:00 committed by GitHub
parent 780ebea3f0
commit dc6a8a6235
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 251 additions and 280 deletions

View File

@ -284,323 +284,294 @@ static rct_widget window_object_load_error_widgets[] = {
#endif
WIDGETS_END,
};
static rct_string_id get_object_type_string(ObjectType type);
static void window_object_load_error_close(rct_window *w);
static void window_object_load_error_update(rct_window *w);
static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex);
static void window_object_load_error_scrollgetsize(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height);
static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi);
static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex);
#ifndef DISABLE_HTTP
static void window_object_load_error_download_all(rct_window* w);
static void window_object_load_error_update_list(rct_window* w);
#endif
static rct_window_event_list window_object_load_error_events([](auto& events)
{
events.close = &window_object_load_error_close;
events.mouse_up = &window_object_load_error_mouseup;
events.update = &window_object_load_error_update;
events.get_scroll_size = &window_object_load_error_scrollgetsize;
events.scroll_mousedown = &window_object_load_error_scrollmousedown;
events.scroll_mouseover = &window_object_load_error_scrollmouseover;
events.paint = &window_object_load_error_paint;
events.scroll_paint = &window_object_load_error_scrollpaint;
});
// clang-format on
static std::vector<ObjectEntryDescriptor> _invalid_entries;
static int32_t highlighted_index = -1;
static std::string file_path;
#ifndef DISABLE_HTTP
static ObjectDownloader _objDownloader;
static bool _updatedListAfterDownload;
#endif
/**
* Returns an rct_string_id that represents an rct_object_entry's type.
*
* Could possibly be moved out of the window file if other
* uses exist and a suitable location is found.
*/
static rct_string_id get_object_type_string(ObjectType type)
static constexpr rct_string_id GetStringFromObjectType(const ObjectType type)
{
rct_string_id result;
switch (type)
{
case ObjectType::Ride:
result = STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS;
break;
return STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS;
case ObjectType::SmallScenery:
result = STR_OBJECT_SELECTION_SMALL_SCENERY;
break;
return STR_OBJECT_SELECTION_SMALL_SCENERY;
case ObjectType::LargeScenery:
result = STR_OBJECT_SELECTION_LARGE_SCENERY;
break;
return STR_OBJECT_SELECTION_LARGE_SCENERY;
case ObjectType::Walls:
result = STR_OBJECT_SELECTION_WALLS_FENCES;
break;
return STR_OBJECT_SELECTION_WALLS_FENCES;
case ObjectType::Banners:
result = STR_OBJECT_SELECTION_PATH_SIGNS;
break;
return STR_OBJECT_SELECTION_PATH_SIGNS;
case ObjectType::Paths:
result = STR_OBJECT_SELECTION_FOOTPATHS;
break;
return STR_OBJECT_SELECTION_FOOTPATHS;
case ObjectType::PathBits:
result = STR_OBJECT_SELECTION_PATH_EXTRAS;
break;
return STR_OBJECT_SELECTION_PATH_EXTRAS;
case ObjectType::SceneryGroup:
result = STR_OBJECT_SELECTION_SCENERY_GROUPS;
break;
return STR_OBJECT_SELECTION_SCENERY_GROUPS;
case ObjectType::ParkEntrance:
result = STR_OBJECT_SELECTION_PARK_ENTRANCE;
break;
return STR_OBJECT_SELECTION_PARK_ENTRANCE;
case ObjectType::Water:
result = STR_OBJECT_SELECTION_WATER;
break;
return STR_OBJECT_SELECTION_WATER;
default:
result = STR_UNKNOWN_OBJECT_TYPE;
return STR_UNKNOWN_OBJECT_TYPE;
}
return result;
}
/**
* Returns a newline-separated string listing all object names.
* Used for placing all names on the clipboard.
*/
static void copy_object_names_to_clipboard(rct_window* w)
class ObjectLoadErrorWindow final : public rct_window
{
std::stringstream stream;
for (uint16_t i = 0; i < w->no_list_items; i++)
private:
std::vector<ObjectEntryDescriptor> _invalidEntries;
int32_t _highlightedIndex = -1;
std::string _filePath;
#ifndef DISABLE_HTTP
ObjectDownloader _objDownloader;
bool _updatedListAfterDownload{};
void DownloadAllObjects()
{
const auto& entry = _invalid_entries[i];
stream << entry.GetName();
stream << PLATFORM_NEWLINE;
if (!_objDownloader.IsDownloading())
{
_updatedListAfterDownload = false;
_objDownloader.Begin(_invalidEntries);
}
}
auto clip = stream.str();
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str());
}
void UpdateObjectList()
{
const auto entries = _objDownloader.GetDownloadedEntries();
for (auto& de : entries)
{
_invalidEntries.erase(
std::remove_if(
_invalidEntries.begin(), _invalidEntries.end(),
[de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }),
_invalidEntries.end());
}
no_list_items = static_cast<uint16_t>(_invalidEntries.size());
}
#endif
/**
* Returns a newline-separated string listing all object names.
* Used for placing all names on the clipboard.
*/
void CopyObjectNamesToClipboard()
{
std::stringstream stream;
for (uint16_t i = 0; i < no_list_items; i++)
{
const auto& entry = _invalidEntries[i];
stream << entry.GetName();
stream << PLATFORM_NEWLINE;
}
const auto clip = stream.str();
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str());
}
void SelectObjectFromList(const int32_t index)
{
if (index < 0 || index > no_list_items)
{
selected_list_item = -1;
}
else
{
selected_list_item = index;
}
widget_invalidate(this, WIDX_SCROLL);
}
public:
void OnOpen() override
{
widgets = window_object_load_error_widgets;
enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL)
| (1ULL << WIDX_DOWNLOAD_ALL);
WindowInitScrollWidgets(this);
colours[0] = COLOUR_LIGHT_BLUE;
colours[1] = COLOUR_LIGHT_BLUE;
colours[2] = COLOUR_LIGHT_BLUE;
}
void OnClose() override
{
_invalidEntries.clear();
_invalidEntries.shrink_to_fit();
}
void OnMouseUp(const rct_widgetindex widgetIndex) override
{
switch (widgetIndex)
{
case WIDX_CLOSE:
window_close(this);
return;
case WIDX_COPY_CURRENT:
if (selected_list_item > -1 && selected_list_item < no_list_items)
{
const auto name = std::string(_invalidEntries[selected_list_item].GetName());
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str());
}
break;
case WIDX_COPY_ALL:
CopyObjectNamesToClipboard();
break;
#ifndef DISABLE_HTTP
case WIDX_DOWNLOAD_ALL:
DownloadAllObjects();
break;
#endif
}
}
void OnUpdate() override
{
frame_no++;
// Check if the mouse is hovering over the list
if (!WidgetIsHighlighted(this, WIDX_SCROLL))
{
_highlightedIndex = -1;
widget_invalidate(this, WIDX_SCROLL);
}
#ifndef DISABLE_HTTP
_objDownloader.Update();
// Remove downloaded objects from our invalid entry list
if (_objDownloader.IsDownloading())
{
// Don't do this too often as it isn't particularly efficient
if (frame_no % 64 == 0)
{
UpdateObjectList();
}
}
else if (!_updatedListAfterDownload)
{
UpdateObjectList();
_updatedListAfterDownload = true;
}
#endif
}
ScreenSize OnScrollGetSize(const int32_t scrollIndex) override
{
return ScreenSize(0, no_list_items * SCROLLABLE_ROW_HEIGHT);
}
void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
SelectObjectFromList(selectedItem);
}
void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
// Highlight item that the cursor is over, or remove highlighting if none
const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
if (selectedItem < 0 || selectedItem >= no_list_items)
_highlightedIndex = -1;
else
_highlightedIndex = selectedItem;
widget_invalidate(this, WIDX_SCROLL);
}
void OnDraw(rct_drawpixelinfo& dpi) override
{
WindowDrawWidgets(this, &dpi);
// Draw explanatory message
auto ft = Formatter();
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_EXPLANATION);
DrawTextWrapped(&dpi, windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft);
// Draw file name
ft = Formatter();
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_FILE);
ft.Add<utf8*>(_filePath.c_str());
DrawTextEllipsised(&dpi, { windowPos.x + 5, windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft);
}
void OnScrollDraw(const int32_t scrollIndex, rct_drawpixelinfo& dpi) override
{
auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y };
gfx_fill_rect(
&dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light);
const int32_t listWidth = widgets[WIDX_SCROLL].width();
for (int32_t i = 0; i < no_list_items; i++)
{
ScreenCoordsXY screenCoords;
screenCoords.y = i * SCROLLABLE_ROW_HEIGHT;
if (screenCoords.y > dpi.y + dpi.height)
break;
if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi.y)
continue;
const auto screenRect = ScreenRect{ { 0, screenCoords.y },
{ listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } };
// If hovering over item, change the color and fill the backdrop.
if (i == selected_list_item)
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].darker);
else if (i == _highlightedIndex)
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].mid_dark);
else if ((i & 1) != 0) // odd / even check
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].light);
// Draw the actual object entry's name...
screenCoords.x = NAME_COL_LEFT - 3;
const auto& entry = _invalidEntries[i];
auto name = entry.GetName();
char buffer[256];
String::Set(buffer, sizeof(buffer), name.data(), name.size());
gfx_draw_string(&dpi, screenCoords, buffer, { COLOUR_DARK_GREEN });
if (entry.Generation == ObjectGeneration::DAT)
{
// ... source game ...
const auto sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame());
DrawTextBasic(&dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN });
}
// ... and type
const auto type = GetStringFromObjectType(entry.GetType());
DrawTextBasic(&dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN });
}
}
void Initialise(utf8* path, const size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects)
{
_invalidEntries = std::vector<ObjectEntryDescriptor>(missingObjects, missingObjects + numMissingObjects);
// Refresh list items and path
no_list_items = static_cast<uint16_t>(numMissingObjects);
_filePath = path;
Invalidate();
}
};
rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects)
{
_invalid_entries = std::vector<ObjectEntryDescriptor>(missingObjects, missingObjects + numMissingObjects);
// Check if window is already open
rct_window* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR);
auto* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR);
if (window == nullptr)
{
window = WindowCreateCentred(WW, WH, &window_object_load_error_events, WC_OBJECT_LOAD_ERROR, 0);
window->widgets = window_object_load_error_widgets;
window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL)
| (1ULL << WIDX_DOWNLOAD_ALL);
WindowInitScrollWidgets(window);
window->colours[0] = COLOUR_LIGHT_BLUE;
window->colours[1] = COLOUR_LIGHT_BLUE;
window->colours[2] = COLOUR_LIGHT_BLUE;
window = WindowCreate<ObjectLoadErrorWindow>(WC_OBJECT_LOAD_ERROR, WW, WH, 0);
}
// Refresh list items and path
window->no_list_items = static_cast<uint16_t>(numMissingObjects);
file_path = path;
static_cast<ObjectLoadErrorWindow*>(window)->Initialise(path, numMissingObjects, missingObjects);
window->Invalidate();
return window;
}
static void window_object_load_error_close(rct_window* w)
{
_invalid_entries.clear();
_invalid_entries.shrink_to_fit();
}
static void window_object_load_error_update(rct_window* w)
{
w->frame_no++;
// Check if the mouse is hovering over the list
if (!WidgetIsHighlighted(w, WIDX_SCROLL))
{
highlighted_index = -1;
widget_invalidate(w, WIDX_SCROLL);
}
#ifndef DISABLE_HTTP
_objDownloader.Update();
// Remove downloaded objects from our invalid entry list
if (_objDownloader.IsDownloading())
{
// Don't do this too often as it isn't particularly efficient
if (w->frame_no % 64 == 0)
{
window_object_load_error_update_list(w);
}
}
else if (!_updatedListAfterDownload)
{
window_object_load_error_update_list(w);
_updatedListAfterDownload = true;
}
#endif
}
static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widgetIndex)
{
switch (widgetIndex)
{
case WIDX_CLOSE:
window_close(w);
break;
case WIDX_COPY_CURRENT:
if (w->selected_list_item > -1 && w->selected_list_item < w->no_list_items)
{
auto name = std::string(_invalid_entries[w->selected_list_item].GetName());
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str());
}
break;
case WIDX_COPY_ALL:
copy_object_names_to_clipboard(w);
break;
#ifndef DISABLE_HTTP
case WIDX_DOWNLOAD_ALL:
window_object_load_error_download_all(w);
break;
#endif
}
}
static void window_object_load_error_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
{
// Highlight item that the cursor is over, or remove highlighting if none
int32_t selected_item;
selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
if (selected_item < 0 || selected_item >= w->no_list_items)
highlighted_index = -1;
else
highlighted_index = selected_item;
widget_invalidate(w, WIDX_SCROLL);
}
static void window_object_load_error_select_element_from_list(rct_window* w, int32_t index)
{
if (index < 0 || index > w->no_list_items)
{
w->selected_list_item = -1;
}
else
{
w->selected_list_item = index;
}
widget_invalidate(w, WIDX_SCROLL);
}
static void window_object_load_error_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
{
int32_t selected_item;
selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
window_object_load_error_select_element_from_list(w, selected_item);
}
static void window_object_load_error_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
{
*height = w->no_list_items * SCROLLABLE_ROW_HEIGHT;
}
static void window_object_load_error_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
WindowDrawWidgets(w, dpi);
// Draw explanatory message
auto ft = Formatter();
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_EXPLANATION);
DrawTextWrapped(dpi, w->windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft);
// Draw file name
ft = Formatter();
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_FILE);
ft.Add<utf8*>(file_path.c_str());
DrawTextEllipsised(dpi, { w->windowPos.x + 5, w->windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft);
}
static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
{
auto dpiCoords = ScreenCoordsXY{ dpi->x, dpi->y };
gfx_fill_rect(
dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi->width - 1, dpi->height - 1 } }, ColourMapA[w->colours[1]].mid_light);
const int32_t list_width = w->widgets[WIDX_SCROLL].width();
for (int32_t i = 0; i < w->no_list_items; i++)
{
ScreenCoordsXY screenCoords;
screenCoords.y = i * SCROLLABLE_ROW_HEIGHT;
if (screenCoords.y > dpi->y + dpi->height)
break;
if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi->y)
continue;
auto screenRect = ScreenRect{ { 0, screenCoords.y }, { list_width, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } };
// If hovering over item, change the color and fill the backdrop.
if (i == w->selected_list_item)
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].darker);
else if (i == highlighted_index)
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].mid_dark);
else if ((i & 1) != 0) // odd / even check
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].light);
// Draw the actual object entry's name...
screenCoords.x = NAME_COL_LEFT - 3;
const auto& entry = _invalid_entries[i];
auto name = entry.GetName();
char buffer[256];
String::Set(buffer, sizeof(buffer), name.data(), name.size());
gfx_draw_string(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN });
if (entry.Generation == ObjectGeneration::DAT)
{
// ... source game ...
rct_string_id sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame());
DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN });
}
// ... and type
rct_string_id type = get_object_type_string(entry.GetType());
DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN });
}
}
#ifndef DISABLE_HTTP
static void window_object_load_error_download_all(rct_window* w)
{
if (!_objDownloader.IsDownloading())
{
_updatedListAfterDownload = false;
_objDownloader.Begin(_invalid_entries);
}
}
static void window_object_load_error_update_list(rct_window* w)
{
auto entries = _objDownloader.GetDownloadedEntries();
for (auto& de : entries)
{
_invalid_entries.erase(
std::remove_if(
_invalid_entries.begin(), _invalid_entries.end(),
[de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }),
_invalid_entries.end());
w->no_list_items = static_cast<uint16_t>(_invalid_entries.size());
}
}
#endif