New dialog presents a yes/no choice with alternate non-exclusive actions

This commit is contained in:
2025-08-02 13:55:01 -05:00
parent fcd2af5e27
commit 44cdf1646a
7 changed files with 194 additions and 4 deletions

View File

@@ -192,6 +192,7 @@
<Xml Include="..\..\..\rsrc\dialogs\welcome.xml" /> <Xml Include="..\..\..\rsrc\dialogs\welcome.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\dialogxml\dialogs\btnpanel.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\keycodes.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\keycodes.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\dialogs\3choice.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\dialogs\3choice.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\dialogs\choicedlog.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\dialogs\choicedlog.cpp" />
@@ -287,6 +288,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\src\alchemy.hpp" /> <ClInclude Include="..\..\..\src\alchemy.hpp" />
<ClInclude Include="..\..\..\src\damage.hpp" /> <ClInclude Include="..\..\..\src\damage.hpp" />
<ClInclude Include="..\..\..\src\dialogxml\dialogs\btnpanel.hpp" />
<ClInclude Include="..\..\..\src\dialogxml\keycodes.hpp" /> <ClInclude Include="..\..\..\src\dialogxml\keycodes.hpp" />
<ClInclude Include="..\..\..\src\dialogxml\dialogs\3choice.hpp" /> <ClInclude Include="..\..\..\src\dialogxml\dialogs\3choice.hpp" />
<ClInclude Include="..\..\..\src\dialogxml\dialogs\choicedlog.hpp" /> <ClInclude Include="..\..\..\src\dialogxml\dialogs\choicedlog.hpp" />
@@ -565,4 +567,4 @@
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\GitInfo.2.0.10\build\GitInfo.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GitInfo.2.0.10\build\GitInfo.targets'))" /> <Error Condition="!Exists('..\packages\GitInfo.2.0.10\build\GitInfo.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GitInfo.2.0.10\build\GitInfo.targets'))" />
</Target> </Target>
</Project> </Project>

View File

@@ -831,6 +831,9 @@
<ClCompile Include="..\..\..\src\scenario\quest.cpp"> <ClCompile Include="..\..\..\src\scenario\quest.cpp">
<Filter>Scenario</Filter> <Filter>Scenario</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\dialogxml\dialogs\btnpanel.cpp">
<Filter>DialogXML\Dialogs</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\src\view_dialogs.hpp" /> <ClInclude Include="..\..\..\src\view_dialogs.hpp" />
@@ -1093,8 +1096,11 @@
<ClInclude Include="..\..\..\src\tools\profile.hpp"> <ClInclude Include="..\..\..\src\tools\profile.hpp">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\..\src\dialogxml\dialogs\btnpanel.hpp">
<Filter>DialogXML\Dialogs</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -344,6 +344,7 @@
<None Include="..\..\..\rsrc\dialogs\dialog.css" /> <None Include="..\..\..\rsrc\dialogs\dialog.css" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\dialogxml\dialogs\btnpanel.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\keycodes.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\keycodes.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\dialogs\3choice.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\dialogs\3choice.cpp" />
<ClCompile Include="..\..\..\src\dialogxml\dialogs\choicedlog.cpp" /> <ClCompile Include="..\..\..\src\dialogxml\dialogs\choicedlog.cpp" />
@@ -441,6 +442,7 @@
<ClCompile Include="..\..\..\src\view_dialogs.cpp" /> <ClCompile Include="..\..\..\src\view_dialogs.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\src\dialogxml\dialogs\btnpanel.hpp" />
<ClInclude Include="..\..\..\src\scenario\town_import.tpp" /> <ClInclude Include="..\..\..\src\scenario\town_import.tpp" />
<ClInclude Include="..\..\..\src\alchemy.hpp" /> <ClInclude Include="..\..\..\src\alchemy.hpp" />
<ClInclude Include="..\..\..\src\damage.hpp" /> <ClInclude Include="..\..\..\src\damage.hpp" />
@@ -618,4 +620,4 @@
</Copy> </Copy>
<Message Text="changed:@(ChangedScenarios)" Importance="high" /> <Message Text="changed:@(ChangedScenarios)" Importance="high" />
</Target> </Target>
</Project> </Project>

View File

@@ -824,6 +824,9 @@
<ClCompile Include="..\..\..\src\scenario\quest.cpp"> <ClCompile Include="..\..\..\src\scenario\quest.cpp">
<Filter>Scenario</Filter> <Filter>Scenario</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\dialogxml\dialogs\btnpanel.cpp">
<Filter>DialogXML\Dialogs</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\src\scenario\town_import.tpp"> <ClInclude Include="..\..\..\src\scenario\town_import.tpp">
@@ -1079,5 +1082,8 @@
<ClInclude Include="..\..\..\src\tools\profile.hpp"> <ClInclude Include="..\..\..\src\tools\profile.hpp">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\..\src\dialogxml\dialogs\btnpanel.hpp">
<Filter>DialogXML\Dialogs</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,16 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog defbtn='done' escbtn='cancel'>
<pict name='pic' type='dlog' num='16' top='8' left='8'/>
<text name='title' size='large' top='6' left='50' width='256' height='14'>Select:</text>
<!-- Column 1 -->
<button type='tiny' name='button1' top='54' left='8'/>
<button type='tiny' name='button2' relative='pos-in pos-in' rel-anchor='prev' top='15' left='0'/>
<button type='tiny' name='button3' relative='pos-in pos-in' rel-anchor='prev' top='15' left='0'/>
<button type='tiny' name='button4' relative='pos-in pos-in' rel-anchor='prev' top='15' left='0'/>
<button type='tiny' name='button5' relative='pos-in pos-in' rel-anchor='prev' top='15' left='0'/>
<button name='left' type='left' def-key='left' relative='pos-in pos-in' anchor='button5' top='19' left='0'/>
<button name='right' type='right' def-key='right' relative='pos-in pos-in' rel-anchor='prev' top='0' left='63'/>
<button name='cancel' type='regular' relative='pos-in pos-in' rel-anchor='prev' top='0' left='101'>Cancel</button>
<button name='done' type='regular' relative='pos-in pos-in' rel-anchor='prev' top='0' left='66'>OK</button>
</dialog>

View File

@@ -0,0 +1,111 @@
#include "btnpanel.hpp"
#include <sstream>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#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<std::string>& strs, std::vector<std::function<void(cButtonPanel&)>> 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<bool>();
}
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;
}

View File

@@ -0,0 +1,47 @@
#ifndef DIALOG_BTNPANEL_H
#define DIALOG_BTNPANEL_H
#include <string>
#include <vector>
#include <functional>
#include <boost/optional.hpp>
#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<std::string> strings;
std::vector<std::function<void(cButtonPanel&)>> 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<std::string>& strs, std::vector<std::function<void(cButtonPanel&)>> 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<std::string> getStrings() const { return strings; }
};
#endif