widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #13797
[Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
GunChleoc has proposed merging lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands.
Commit message:
Converted editor infotool to new font renderer and added some layout tweaks. Pulled out common layout functions into text_layout.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/editor_infotool_rendering/+merge/349093
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands.
=== modified file 'src/editor/tools/info_tool.cc'
--- src/editor/tools/info_tool.cc 2018-04-27 06:11:05 +0000
+++ src/editor/tools/info_tool.cc 2018-07-08 09:28:57 +0000
@@ -26,6 +26,7 @@
#include "base/i18n.h"
#include "editor/editorinteractive.h"
+#include "graphic/text_layout.h"
#include "logic/map_objects/world/editor_category.h"
#include "logic/map_objects/world/terrain_description.h"
#include "ui_basic/multilinetextarea.h"
@@ -37,21 +38,21 @@
EditorInteractive& parent,
EditorActionArgs* /* args */,
Widelands::Map* map) {
+
+ static constexpr int kListFontsize = UI_FONT_SIZE_MESSAGE;
parent.stop_painting();
UI::Window* const w =
new UI::Window(&parent, "field_information", 30, 30, 400, 200, _("Field Information"));
UI::MultilineTextarea* const multiline_textarea =
new UI::MultilineTextarea(w, 0, 0, w->get_inner_w(), w->get_inner_h(), UI::PanelStyle::kWui);
+ multiline_textarea->force_new_renderer();
Widelands::Field& f = (*map)[center.node];
// *** Node info
- std::string buf = _("Node:");
- buf += "\n";
- buf += "• " +
- (boost::format(_("Coordinates: (%1$i, %2$i)")) % center.node.x % center.node.y).str() +
- "\n";
+ std::string buf = as_heading(_("Node"), UI::PanelStyle::kWui, true);
+ buf += as_listitem((boost::format(_("Coordinates: (%1$i, %2$i)")) % center.node.x % center.node.y).str(), kListFontsize);
std::vector<std::string> caps_strings;
Widelands::NodeCaps const caps = f.nodecaps();
@@ -95,92 +96,122 @@
caps_strings.push_back(_("swimmable"));
}
- buf += std::string("• ") +
+ buf += as_listitem(
(boost::format(_("Caps: %s")) %
i18n::localize_list(caps_strings, i18n::ConcatenateWith::COMMA))
- .str() +
- "\n";
+ .str(), kListFontsize);
if (f.get_owned_by() > 0) {
- buf += std::string("• ") +
+ buf += as_listitem(
(boost::format(_("Owned by: Player %u")) % static_cast<unsigned int>(f.get_owned_by()))
- .str() +
- "\n";
+ .str(), kListFontsize);
} else {
- buf += std::string("• ") + _("Owned by: —") + "\n";
+ buf += as_listitem(_("Owned by: —"), kListFontsize);
}
- std::string temp = f.get_immovable() ? _("Has immovable") : _("No immovable");
- buf += "• " + temp + "\n";
-
- temp = f.get_first_bob() ? _("Has animals") : _("No animals");
- buf += "• " + temp + "\n";
-
// *** Terrain info
- buf += std::string("\n") + _("Terrain:") + "\n";
+ buf += as_heading(_("Terrain"), UI::PanelStyle::kWui);
const Widelands::Field& tf = (*map)[center.triangle.node];
const Widelands::TerrainDescription& ter = world.terrain_descr(
center.triangle.t == Widelands::TriangleIndex::D ? tf.terrain_d() : tf.terrain_r());
buf +=
- "• " + (boost::format(pgettext("terrain_name", "Name: %s")) % ter.descname()).str() + "\n";
+ as_listitem((boost::format(pgettext("terrain_name", "Name: %s")) % ter.descname()).str(), kListFontsize);
std::vector<std::string> terrain_is_strings;
for (const Widelands::TerrainDescription::Type& terrain_type : ter.get_types()) {
terrain_is_strings.push_back(terrain_type.descname);
}
- buf += "• " +
- /** TRANSLATORS: "Is" is a list of terrain properties, e.g. "arable", "unreachable and
- * unwalkable" */
- /** TRANSLATORS: You can also translate this as "Category: %s" or "Property: %s" */
+ buf += as_listitem(
+ /** TRANSLATORS: "Is" is a list of terrain properties, e.g. "arable, unreachable and
+ * unwalkable". You can also translate this as "Category: %s" or "Property: %s" */
(boost::format(_("Is: %s")) %
i18n::localize_list(terrain_is_strings, i18n::ConcatenateWith::AMPERSAND))
- .str() +
- "\n";
- buf += "• " +
- (boost::format(_("Editor Category: %s")) % ter.editor_category()->descname()).str() +
- "\n";
+ .str(), kListFontsize);
+ buf += as_listitem(
+ (boost::format(_("Editor Category: %s")) % ter.editor_category()->descname()).str(), kListFontsize);
+
+ // *** Map Object info
+ const Widelands::BaseImmovable* immovable = f.get_immovable();
+ Widelands::Bob* bob =f.get_first_bob();
+ if (immovable || bob) {
+ /** TRANSLATORS: Heading for immovables and animals in editor info tool */
+ buf += as_heading(_("Objects"), UI::PanelStyle::kWui);
+ if (immovable) {
+ buf += as_listitem(
+ (boost::format(_("Immovable: %s")) % immovable->descr().descname()).str(), kListFontsize);
+ }
+
+ if (bob) {
+ // Collect bobs
+ std::vector<std::string> critternames;
+ std::vector<std::string> shipnames;
+ std::vector<std::string> workernames;
+ do {
+ switch (bob->descr().type()) {
+ case (Widelands::MapObjectType::CRITTER):
+ critternames.push_back(bob->descr().descname());
+ break;
+ case (Widelands::MapObjectType::SHIP): {
+ if (upcast(Widelands::Ship, ship, bob)) {
+ shipnames.push_back(ship->get_shipname());
+ }
+ } break;
+ case (Widelands::MapObjectType::WORKER):
+ case (Widelands::MapObjectType::CARRIER):
+ case (Widelands::MapObjectType::SOLDIER):
+ workernames.push_back(bob->descr().descname());
+ break;
+ default:
+ break;
+ }
+ } while ((bob = bob->get_next_bob()));
+
+ // Add bobs
+ if (!critternames.empty()) {
+ buf += as_listitem(
+ (boost::format(_("Animals: %s")) % i18n::localize_list(critternames, i18n::ConcatenateWith::COMMA)).str(), kListFontsize);
+ }
+ if (!workernames.empty()) {
+ buf += as_listitem(
+ (boost::format(_("Workers: %s")) % i18n::localize_list(workernames, i18n::ConcatenateWith::COMMA)).str(), kListFontsize);
+ }
+ if (!workernames.empty()) {
+ buf += as_listitem(
+ (boost::format(_("Ships: %s")) % i18n::localize_list(shipnames, i18n::ConcatenateWith::COMMA)).str(), kListFontsize);
+ }
+ }
+ }
// *** Resources info
- buf += std::string("\n") + _("Resources:") + "\n";
-
- Widelands::DescriptionIndex ridx = f.get_resources();
Widelands::ResourceAmount ramount = f.get_resources_amount();
-
if (ramount > 0) {
+ buf += as_heading(_("Resources"), UI::PanelStyle::kWui);
buf +=
- "• " +
- (boost::format(_("Resource name: %s")) % world.get_resource(ridx)->name().c_str()).str() +
- "\n";
- buf += "• " +
- (boost::format(_("Resource amount: %i")) % static_cast<unsigned int>(ramount)).str() +
- "\n";
- } else {
- buf += "• " + std::string(_("No resources")) + "\n";
+ as_listitem(
+ (boost::format(pgettext("resources", "%1%x %2%")) % static_cast<unsigned int>(ramount) % world.get_resource(f.get_resources())->descname()).str(), kListFontsize);
}
// *** Map info
- buf += std::string("\n") + _("Map:") + "\n";
- buf += "• " + (boost::format(pgettext("map_name", "Name: %s")) % map->get_name().c_str()).str() +
- "\n";
- buf += "• " +
- (boost::format(_("Size: %1$ix%2$i")) % map->get_width() % map->get_height()).str() + "\n";
+ buf += as_heading(_("Map"), UI::PanelStyle::kWui);
+ buf += as_listitem((boost::format(pgettext("map_name", "Name: %s")) % map->get_name()).str(), kListFontsize);
+ buf += as_listitem(
+ (boost::format(_("Size: %1% x %2%")) % map->get_width() % map->get_height()).str(), kListFontsize);
if (map->get_nrplayers() > 0) {
buf +=
- "• " +
- (boost::format(_("Players: %u")) % static_cast<unsigned int>(map->get_nrplayers())).str() +
- "\n";
+ as_listitem(
+ (boost::format(_("Players: %u")) % static_cast<unsigned int>(map->get_nrplayers())).str(), kListFontsize);
} else {
- buf += "• " + std::string(_("Players: –")) + "\n";
+ buf += as_listitem(_("Players: –"), kListFontsize);
}
- buf += "• " + (boost::format(_("Author: %s")) % map->get_author()).str() + "\n";
- buf += "• " + (boost::format(_("Descr: %s")) % map->get_description().c_str()).str() + "\n";
+ buf += as_listitem((boost::format(_("Author: %s")) % map->get_author()).str(), kListFontsize);
+ buf += as_listitem((boost::format(_("Description: %s")) % map->get_description()).str(), kListFontsize);
- multiline_textarea->set_text(buf.c_str());
+ multiline_textarea->set_text(as_richtext(buf));
return 0;
}
=== modified file 'src/graphic/text_layout.cc'
--- src/graphic/text_layout.cc 2018-06-01 08:54:25 +0000
+++ src/graphic/text_layout.cc 2018-07-08 09:28:57 +0000
@@ -189,6 +189,57 @@
.str());
}
+std::string as_heading_with_content(const std::string& header,
+ const std::string& content,
+ UI::PanelStyle style,
+ bool is_first,
+ bool noescape) {
+ switch (style) {
+ case UI::PanelStyle::kFsMenu:
+ return (boost::format(
+ "<p><font size=%i bold=1 shadow=1>%s%s <font color=D1D1D1>%s</font></font></p>") %
+ UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=9>") %
+ (noescape ? header : richtext_escape(header)) %
+ (noescape ? content : richtext_escape(content)))
+ .str();
+ case UI::PanelStyle::kWui:
+ return (boost::format(
+ "<p><font size=%i>%s<font bold=1 color=D1D1D1>%s</font> %s</font></p>") %
+ UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=6>") %
+ (noescape ? header : richtext_escape(header)) %
+ (noescape ? content : richtext_escape(content)))
+ .str();
+ }
+ NEVER_HERE();
+}
+
+std::string as_heading(const std::string& txt, UI::PanelStyle style, bool is_first) {
+ switch (style) {
+ case UI::PanelStyle::kFsMenu:
+ return (boost::format("<p><font size=%i bold=1 shadow=1>%s%s</font></p>") %
+ UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=9>") % richtext_escape(txt))
+ .str();
+ case UI::PanelStyle::kWui:
+ return (boost::format("<p><font size=%i bold=1 color=D1D1D1>%s%s</font></p>") %
+ UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=6>") % richtext_escape(txt))
+ .str();
+ }
+ NEVER_HERE();
+}
+std::string as_content(const std::string& txt, UI::PanelStyle style) {
+ switch (style) {
+ case UI::PanelStyle::kFsMenu:
+ return (boost::format("<p><font size=%i color=D1D1D1 shadow=1><vspace gap=2>%s</font></p>") %
+ UI_FONT_SIZE_SMALL % richtext_escape(txt))
+ .str();
+ case UI::PanelStyle::kWui:
+ return (boost::format("<p><font size=%i><vspace gap=2>%s</font></p>") %
+ (UI_FONT_SIZE_SMALL - 2) % richtext_escape(txt))
+ .str();
+ }
+ NEVER_HERE();
+}
+
std::shared_ptr<const UI::RenderedText>
autofit_ui_text(const std::string& text, int width, RGBColor color, int fontsize) {
std::shared_ptr<const UI::RenderedText> result =
=== modified file 'src/graphic/text_layout.h'
--- src/graphic/text_layout.h 2018-06-01 08:50:29 +0000
+++ src/graphic/text_layout.h 2018-07-08 09:28:57 +0000
@@ -26,6 +26,7 @@
#include "graphic/color.h"
#include "graphic/font_handler1.h"
#include "graphic/image.h"
+#include "graphic/panel_styles.h"
#include "graphic/text/font_set.h"
#include "graphic/text_constants.h"
@@ -95,6 +96,21 @@
std::string as_message(const std::string& heading, const std::string& body);
/**
+ * 'is_first' omits the vertical gap before the line.
+ * 'noescape' is needed for error message formatting and does not call richtext_escape. */
+std::string as_heading_with_content(const std::string& header,
+ const std::string& content,
+ UI::PanelStyle style,
+ bool is_first = false,
+ bool noescape = false);
+
+/**
+ * 'is_first' omits the vertical gap before the line.
+ */
+std::string as_heading(const std::string& txt, UI::PanelStyle style, bool is_first = false);
+std::string as_content(const std::string& txt, UI::PanelStyle style);
+
+/**
* Render 'text' as ui_font. If 'width' > 0 and the rendered image is too
* wide, it will first use the condensed font face and then make the text
* smaller until it fits 'width'. The resulting font size will not go below
=== modified file 'src/ui_fsmenu/launch_mpg.cc'
--- src/ui_fsmenu/launch_mpg.cc 2018-05-26 16:16:46 +0000
+++ src/ui_fsmenu/launch_mpg.cc 2018-07-08 09:28:57 +0000
@@ -584,7 +584,7 @@
std::string infotext;
infotext += std::string(_("Map details:")) + "\n";
infotext += std::string("• ") +
- (boost::format(_("Size: %1$u x %2$u")) % map.get_width() % map.get_height()).str() +
+ (boost::format(_("Size: %1% x %2%")) % map.get_width() % map.get_height()).str() +
"\n";
infotext += std::string("• ") +
(boost::format(ngettext("%u Player", "%u Players", nr_players_)) %
=== modified file 'src/wui/gamedetails.cc'
--- src/wui/gamedetails.cc 2018-06-04 19:18:34 +0000
+++ src/wui/gamedetails.cc 2018-07-08 09:28:57 +0000
@@ -34,35 +34,6 @@
#include "io/filesystem/layered_filesystem.h"
// TODO(GunChleoc): Arabic: line height broken for descriptions for Arabic.
-namespace {
-// 'is_first' omits the vertical gap before the line.
-// 'noescape' is needed for error message formatting and does not call richtext_escape.
-std::string as_header_with_content(const std::string& header,
- const std::string& content,
- UI::PanelStyle style,
- bool is_first = false,
- bool noescape = false) {
- switch (style) {
- case UI::PanelStyle::kFsMenu:
- return (boost::format(
- "<p><font size=%i bold=1 shadow=1>%s%s <font color=D1D1D1>%s</font></font></p>") %
- UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=9>") %
- (noescape ? header : richtext_escape(header)) %
- (noescape ? content : richtext_escape(content)))
- .str();
- case UI::PanelStyle::kWui:
- return (boost::format(
- "<p><font size=%i>%s<font bold=1 color=D1D1D1>%s</font> %s</font></p>") %
- UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=6>") %
- (noescape ? header : richtext_escape(header)) %
- (noescape ? content : richtext_escape(content)))
- .str();
- }
- NEVER_HERE();
-}
-
-} // namespace
-
SavegameData::SavegameData()
: gametime(""),
nrplayers("0"),
@@ -141,12 +112,11 @@
if (gamedata.errormessage.empty()) {
if (gamedata.filename_list.empty()) {
name_label_.set_text(
- (boost::format("<rt>%s</rt>") %
- as_header_with_content(_("Map Name:"), gamedata.mapname, style_, true))
- .str());
+ as_richtext(
+ as_heading_with_content(_("Map Name:"), gamedata.mapname, style_, true)));
// Show game information
- std::string description = as_header_with_content(
+ std::string description = as_heading_with_content(
mode_ == Mode::kReplay ?
/** TRANSLATORS: The time a replay starts. Shown in the replay loading screen*/
_("Start of Replay:") :
@@ -156,15 +126,15 @@
gamedata.gametime, style_);
description = (boost::format("%s%s") % description %
- as_header_with_content(_("Players:"), gamedata.nrplayers, style_))
- .str();
-
- description = (boost::format("%s%s") % description %
- as_header_with_content(_("Widelands Version:"), gamedata.version, style_))
- .str();
-
- description = (boost::format("%s%s") % description %
- as_header_with_content(_("Win Condition:"), gamedata.wincondition, style_))
+ as_heading_with_content(_("Players:"), gamedata.nrplayers, style_))
+ .str();
+
+ description = (boost::format("%s%s") % description %
+ as_heading_with_content(_("Widelands Version:"), gamedata.version, style_))
+ .str();
+
+ description = (boost::format("%s%s") % description %
+ as_heading_with_content(_("Win Condition:"), gamedata.wincondition, style_))
.str();
std::string filename = gamedata.filename;
@@ -173,11 +143,10 @@
filename.erase(0, filename.find('/') + 1);
assert(!filename.empty());
description = (boost::format("%s%s") % description %
- as_header_with_content(_("Filename:"), filename, style_))
+ as_heading_with_content(_("Filename:"), filename, style_))
.str();
- description = (boost::format("<rt>%s</rt>") % description).str();
- descr_.set_text(description);
+ descr_.set_text(as_richtext(description));
std::string minimap_path = gamedata.minimap_path;
if (!minimap_path.empty()) {
@@ -195,20 +164,16 @@
} else {
std::string filename_list = richtext_escape(gamedata.filename_list);
boost::replace_all(filename_list, "\n", "<br> • ");
- name_label_.set_text((boost::format("<rt>%s</rt>") %
- as_header_with_content(gamedata.mapname, "", style_, true))
- .str());
+ name_label_.set_text(as_richtext(
+ as_heading_with_content(gamedata.mapname, "", style_, true)));
- descr_.set_text((boost::format("<rt>%s</rt>") %
- as_header_with_content("", filename_list, style_, true, true))
- .str());
+ descr_.set_text(as_richtext(
+ as_heading_with_content("", filename_list, style_, true, true)));
minimap_icon_.set_visible(false);
}
} else {
name_label_.set_text(
- (boost::format("<rt>%s</rt>") %
- as_header_with_content(_("Error:"), gamedata.errormessage, style_, true, true))
- .str());
+ as_richtext(as_heading_with_content(_("Error:"), gamedata.errormessage, style_, true, true)));
}
layout();
}
=== modified file 'src/wui/mapdetails.cc'
--- src/wui/mapdetails.cc 2018-04-27 06:11:05 +0000
+++ src/wui/mapdetails.cc 2018-07-08 09:28:57 +0000
@@ -37,35 +37,6 @@
#include "ui_basic/scrollbar.h"
#include "wui/map_tags.h"
-namespace {
-std::string as_header(const std::string& txt, UI::PanelStyle style, bool is_first = false) {
- switch (style) {
- case UI::PanelStyle::kFsMenu:
- return (boost::format("<p><font size=%i bold=1 shadow=1>%s%s</font></p>") %
- UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=9>") % richtext_escape(txt))
- .str();
- case UI::PanelStyle::kWui:
- return (boost::format("<p><font size=%i bold=1 color=D1D1D1>%s%s</font></p>") %
- UI_FONT_SIZE_SMALL % (is_first ? "" : "<vspace gap=6>") % richtext_escape(txt))
- .str();
- }
- NEVER_HERE();
-}
-std::string as_content(const std::string& txt, UI::PanelStyle style) {
- switch (style) {
- case UI::PanelStyle::kFsMenu:
- return (boost::format("<p><font size=%i color=D1D1D1 shadow=1><vspace gap=2>%s</font></p>") %
- UI_FONT_SIZE_SMALL % richtext_escape(txt))
- .str();
- case UI::PanelStyle::kWui:
- return (boost::format("<p><font size=%i><vspace gap=2>%s</font></p>") %
- (UI_FONT_SIZE_SMALL - 2) % richtext_escape(txt))
- .str();
- }
- NEVER_HERE();
-}
-} // namespace
-
MapDetails::MapDetails(
Panel* parent, int32_t x, int32_t y, int32_t w, int32_t h, UI::PanelStyle style)
: UI::Panel(parent, x, y, w, h),
@@ -119,7 +90,7 @@
// Show directory information
if (mapdata.maptype == MapData::MapType::kDirectory) {
name_label_.set_text((boost::format("<rt>%s%s</rt>") %
- as_header(_("Directory:"), style_, true) %
+ as_heading(_("Directory:"), style_, true) %
as_content(mapdata.localized_name, style_))
.str());
main_box_.set_size(main_box_.get_w(), get_h());
@@ -127,7 +98,7 @@
} else { // Show map information
name_label_.set_text(
(boost::format("<rt>%s%s</rt>") %
- as_header(mapdata.maptype == MapData::MapType::kScenario ? _("Scenario:") : _("Map:"),
+ as_heading(mapdata.maptype == MapData::MapType::kScenario ? _("Scenario:") : _("Map:"),
style_, true) %
as_content(localize_mapname ? mapdata.localized_name : mapdata.name, style_))
.str());
@@ -154,7 +125,7 @@
// Show map information
std::string description =
- as_header(ngettext("Author:", "Authors:", mapdata.authors.get_number()), style_);
+ as_heading(ngettext("Author:", "Authors:", mapdata.authors.get_number()), style_);
description =
(boost::format("%s%s") % description % as_content(mapdata.authors.get_names(), style_))
.str();
@@ -164,25 +135,24 @@
tags.push_back(localize_tag(tag));
}
std::sort(tags.begin(), tags.end());
- description = (boost::format("%s%s") % description % as_header(_("Tags:"), style_)).str();
+ description = (boost::format("%s%s") % description % as_heading(_("Tags:"), style_)).str();
description = (boost::format("%s%s") % description %
as_content(i18n::localize_list(tags, i18n::ConcatenateWith::COMMA), style_))
.str();
description =
- (boost::format("%s%s") % description % as_header(_("Description:"), style_)).str();
+ (boost::format("%s%s") % description % as_heading(_("Description:"), style_)).str();
description =
(boost::format("%s%s") % description % as_content(mapdata.description, style_)).str();
if (!mapdata.hint.empty()) {
/** TRANSLATORS: Map hint header when selecting a map. */
- description = (boost::format("%s%s") % description % as_header(_("Hint:"), style_)).str();
+ description = (boost::format("%s%s") % description % as_heading(_("Hint:"), style_)).str();
description =
(boost::format("%s%s") % description % as_content(mapdata.hint, style_)).str();
}
- description = (boost::format("<rt>%s</rt>") % description).str();
- descr_.set_text(description);
+ descr_.set_text(as_richtext(description));
// Show / hide suggested teams
if (mapdata.suggested_teams.empty()) {
Follow ups
-
[Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: noreply, 2018-07-19
-
[Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: bunnybot, 2018-07-19
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: GunChleoc, 2018-07-19
-
[Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: bunnybot, 2018-07-18
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: GunChleoc, 2018-07-14
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: kaputtnik, 2018-07-14
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: GunChleoc, 2018-07-14
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: Klaus Halfmann, 2018-07-14
-
Re: [Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: Klaus Halfmann, 2018-07-14
-
[Merge] lp:~widelands-dev/widelands/editor_infotool_rendering into lp:widelands
From: bunnybot, 2018-07-08