diff --git a/proj/vs2013/Common/Common.vcxproj b/proj/vs2013/Common/Common.vcxproj
index 3db02b12..b9be0f8f 100644
--- a/proj/vs2013/Common/Common.vcxproj
+++ b/proj/vs2013/Common/Common.vcxproj
@@ -192,6 +192,7 @@
+
@@ -287,6 +288,7 @@
+
@@ -565,4 +567,4 @@
-
+
\ No newline at end of file
diff --git a/proj/vs2013/Common/Common.vcxproj.filters b/proj/vs2013/Common/Common.vcxproj.filters
index c435cdb1..c3281092 100644
--- a/proj/vs2013/Common/Common.vcxproj.filters
+++ b/proj/vs2013/Common/Common.vcxproj.filters
@@ -831,6 +831,9 @@
Scenario
+
+ DialogXML\Dialogs
+
@@ -1093,8 +1096,11 @@
Tools
+
+ DialogXML\Dialogs
+
-
+
\ No newline at end of file
diff --git a/proj/vs2017/Common/Common.vcxproj b/proj/vs2017/Common/Common.vcxproj
index 082b7c0b..3341613c 100644
--- a/proj/vs2017/Common/Common.vcxproj
+++ b/proj/vs2017/Common/Common.vcxproj
@@ -344,6 +344,7 @@
+
@@ -441,6 +442,7 @@
+
@@ -618,4 +620,4 @@
-
+
\ No newline at end of file
diff --git a/proj/vs2017/Common/Common.vcxproj.filters b/proj/vs2017/Common/Common.vcxproj.filters
index 30cb6bea..c55922d1 100644
--- a/proj/vs2017/Common/Common.vcxproj.filters
+++ b/proj/vs2017/Common/Common.vcxproj.filters
@@ -824,6 +824,9 @@
Scenario
+
+ DialogXML\Dialogs
+
@@ -1079,5 +1082,8 @@
Tools
+
+ DialogXML\Dialogs
+
-
+
\ No newline at end of file
diff --git a/rsrc/dialogs/tiny-button-panel.xml b/rsrc/dialogs/tiny-button-panel.xml
new file mode 100644
index 00000000..246eed58
--- /dev/null
+++ b/rsrc/dialogs/tiny-button-panel.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/src/dialogxml/dialogs/btnpanel.cpp b/src/dialogxml/dialogs/btnpanel.cpp
new file mode 100644
index 00000000..086398cb
--- /dev/null
+++ b/src/dialogxml/dialogs/btnpanel.cpp
@@ -0,0 +1,111 @@
+#include "btnpanel.hpp"
+
+#include
+#include
+
+#include
+#include
+
+#include "dialogxml/widgets/field.hpp"
+#include "dialogxml/dialogs/strdlog.hpp"
+#include "fileio/resmgr/res_dialog.hpp"
+#include "sounds.hpp"
+#include "gfx/render_shapes.hpp"
+#include "tools/cursors.hpp"
+
+cButtonPanel::cButtonPanel(cDialog* parent)
+ : dlg(*ResMgr::dialogs.get("tiny-button-panel"), parent)
+{}
+
+cButtonPanel::cButtonPanel(const std::vector& strs, std::vector> click_handlers, std::string title, std::string ok_str, cDialog* parent)
+ : cButtonPanel(parent)
+{
+ setTitle(title);
+ if(!ok_str.empty()){
+ dlg["done"].setText(ok_str);
+ }
+ strings = strs;
+ this->click_handlers = click_handlers;
+ attachHandlers();
+}
+
+void cButtonPanel::attachHandlers() {
+ using namespace std::placeholders;
+ dlg["left"].attachClickHandler(std::bind(&cButtonPanel::onLeft,this));
+ dlg["right"].attachClickHandler(std::bind(&cButtonPanel::onRight,this));
+ dlg["done"].attachClickHandler(std::bind(&cButtonPanel::onOkay,this,_1));
+ dlg["cancel"].attachClickHandler(std::bind(&cButtonPanel::onCancel,this,_1));
+ if(strings.size() <= per_page) {
+ dlg["left"].hide();
+ dlg["right"].hide();
+ }
+ // Attach click handler to the tiny buttons
+ for(int i = 0; i < per_page; ++i){
+ short string_idx = page * per_page + i;
+ std::ostringstream sout;
+ sout << "button" << i + 1;
+ dlg[sout.str()].attachClickHandler([i, this](cDialog&,std::string,eKeyMod) -> bool {
+ click_handlers[i](*this);
+ return true;
+ });
+ }
+}
+
+cDialog* cButtonPanel::operator->() {
+ return &dlg;
+}
+
+bool cButtonPanel::show() {
+ page = 0;
+ dlg.run(std::bind(&cButtonPanel::fillPage, this));
+ return dlg.getResult();
+}
+
+void cButtonPanel::fillPage(){
+ for(unsigned int i = 0; i < per_page; i++){
+ short string_idx = page * per_page + i;
+ std::ostringstream sout;
+ sout << "button" << i + 1;
+ if(string_idx < strings.size()){
+ dlg[sout.str()].setText(strings[string_idx]);
+ dlg[sout.str()].recalcRect();
+ dlg[sout.str()].show();
+ }else{
+ dlg[sout.str()].hide();
+ }
+ }
+}
+
+bool cButtonPanel::onLeft(){
+ if(page == 0) page = lastPage();
+ else page--;
+ fillPage();
+ return true;
+}
+
+bool cButtonPanel::onRight(){
+ if(page == lastPage()) page = 0;
+ else page++;
+ fillPage();
+ return true;
+}
+
+bool cButtonPanel::onCancel(cDialog& me){
+ dlg.setResult(false);
+ me.toast(false);
+ return true;
+}
+
+bool cButtonPanel::onOkay(cDialog& me){
+ dlg.setResult(true);
+ me.toast(true);
+ return true;
+}
+
+void cButtonPanel::setTitle(const std::string &title) {
+ if(!title.empty()) dlg["title"].setText(title);
+}
+
+size_t cButtonPanel::lastPage() const {
+ return (strings.size() - 1) / per_page;
+}
\ No newline at end of file
diff --git a/src/dialogxml/dialogs/btnpanel.hpp b/src/dialogxml/dialogs/btnpanel.hpp
new file mode 100644
index 00000000..90317296
--- /dev/null
+++ b/src/dialogxml/dialogs/btnpanel.hpp
@@ -0,0 +1,47 @@
+#ifndef DIALOG_BTNPANEL_H
+#define DIALOG_BTNPANEL_H
+
+#include
+#include
+#include
+#include
+#include "dialog.hpp"
+#include "dialogxml/widgets/ledgroup.hpp"
+
+/// A dialog that presents a list of labeled tiny buttons, plus an OK and Cancel button.
+/// The list may span several pages.
+class cButtonPanel {
+ const size_t per_page = 5;
+ cDialog dlg;
+ bool onLeft();
+ bool onRight();
+ bool onCancel(cDialog& me);
+ bool onOkay(cDialog& me);
+ void attachHandlers();
+ void fillPage();
+ size_t lastPage() const;
+ std::vector strings;
+ std::vector> click_handlers;
+ size_t page, cur;
+ cButtonPanel(cDialog* parent);
+public:
+ /// Initializes a dialog from a list of strings.
+ /// @param strs A list of all strings in the dialog.
+ /// @param click_handlers A list of click handlers for the strings
+ /// @param title The title to show in the dialog.
+ /// @param parent Optionally, a parent dialog.
+ explicit cButtonPanel(const std::vector& strs, std::vector> click_handlers, std::string title, std::string ok_str = "", cDialog* parent = nullptr);
+ /// Reference the cDialog powering this choice dialog, perhaps to customize details of it.
+ /// @return A pointer to the dialog.
+ cDialog* operator->();
+ /// Show the dialog.
+ /// @return True if OK was pressed, false if Cancel pressed
+ bool show();
+ /// Set the dialog's title.
+ /// @param title The new title.
+ void setTitle(const std::string& title);
+ /// Get the list of strings.
+ std::vector getStrings() const { return strings; }
+};
+
+#endif