Compare commits

...

8 commits

Author SHA1 Message Date
Ralph Sennhauser
71400e8045
Drop unused lowlevel functions
and associated types. Aka posix dirent.h abstraction.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-06-14 21:58:59 +02:00
Ralph Sennhauser
43e7dbc6da
Use std::filesystem for filesystem abstraction
Drop the use of lowlevel interfaces and consitently use the cpp
interface decupling it from systm dependent code.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-06-14 21:58:59 +02:00
phosit
2f2cbb96bf
Remove this.isDeserialized from BaseAI
Some checks are pending
checkrefs / lfscheck (push) Waiting to run
checkrefs / checkrefs (push) Waiting to run
lint / cppcheck (push) Waiting to run
lint / copyright (push) Waiting to run
lint / jenkinsfiles (push) Waiting to run
pre-commit / build (push) Waiting to run
`this.isDeserialized` is only required for AIs which `Deserialize`
function depends on the game state.
2026-06-14 17:52:54 +02:00
phosit
a4b580991b
Move this.turn to PetraBot
The `BaseAI` shouldn't make assumptions whether the AI runs every turn.
2026-06-14 17:52:54 +02:00
phosit
babe9e5c18
Remove this.events from AIs
`this.events` is only valid during one turn. When it is used in the next
turnn it might be outdated. So it makes no sense to keep it alive untill
the next turn.
2026-06-14 17:52:54 +02:00
phosit
cbce748b8c
Remove moduleName from Petras data.json
It was accidentally re-introduced in 88dc947b6a.
2026-06-14 13:21:26 +02:00
Ralph Sennhauser
1ab55e7f2e
Remove leftover code from wxCollapsiblePane use
There is a workaround for an old bug with the use of wxCollapsiblePane.
As the widget itself is no longer in use, just remove the now dead code.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-06-13 18:34:09 +02:00
Ralph Sennhauser
b4fe426963
Replace notebook with choicebook
The terrain selection notebook is painful to navigate with a large
amount of tabs. Further the control is broken on macOS. As such replace
the wxNotebook with a wxChoicebook.

Fixes: #8705
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-06-13 16:57:32 +02:00
12 changed files with 136 additions and 414 deletions

View file

@ -6,9 +6,6 @@ export function BaseAI(settings)
return;
this.player = settings.player;
// played turn, in case you don't want the AI to play every turn.
this.turn = 0;
}
/** Return a simple object (using no classes etc) that will be serialized into saved games */
@ -23,7 +20,6 @@ BaseAI.prototype.Serialize = function()
*/
BaseAI.prototype.Deserialize = function(data, sharedScript)
{
this.isDeserialized = true;
};
BaseAI.prototype.Init = function(state, playerID, sharedAI)
@ -49,14 +45,7 @@ BaseAI.prototype.CustomInit = function()
BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI)
{
PlayerID = playerID;
this.events = sharedAI.events;
this.territoryMap = sharedAI.territoryMap;
if (this.isDeserialized)
{
this.Init(state, playerID, sharedAI);
this.isDeserialized = false;
}
this.OnUpdate(sharedAI);
};

View file

@ -9,6 +9,8 @@ export function PetraBot(settings)
{
BaseAI.call(this, settings);
// played turn, because Petra doesn't play every turn.
this.turn = 0;
this.playedTurn = 0;
this.elapsedTime = 0;
@ -89,17 +91,20 @@ PetraBot.prototype.CustomInit = function(gameState)
PetraBot.prototype.OnUpdate = function(sharedScript)
{
if (this.isDeserialized)
this.Init(state, playerID, sharedAI);
if (this.gameFinished || this.gameState.playerData.state == "defeated")
return;
for (const i in this.events)
for (const i in sharedScript.events)
{
if (i == "AIMetadata") // not used inside petra
continue;
if (this.savedEvents[i] !== undefined)
this.savedEvents[i] = this.savedEvents[i].concat(this.events[i]);
this.savedEvents[i] = this.savedEvents[i].concat(sharedScript.events[i]);
else
this.savedEvents[i] = this.events[i];
this.savedEvents[i] = sharedScript.events[i];
}
// Run the update every n turns, offset depending on player ID to balance the load

View file

@ -1,7 +1,6 @@
{
"name": "Petra Bot",
"description": "Petra is the default 0 A.D. AI bot. Please report issues to Wildfire Games.\n\nThe AI has six levels of difficulty: Sandbox, Very Easy, Easy, Medium, Hard, and Very Hard. Besides adjusting its strategy depending on the level, the AI also receives advantages or handycaps for certain rates, such as trade gain and efficiency of resource gathering. The AI can be quite challenging for beginners, so it is recommended to start with Very Easy or Easy until until you feel confident.",
"moduleName" : "PETRA",
"constructor": "PetraBot",
"filename": "_petrabot.js",
"useShared": true

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -26,28 +26,23 @@
#include "precompiled.h"
#include "file_system.h"
#include "lib/debug.h"
#include "lib/file/file_system.h"
#include "lib/posix/posix_filesystem.h"
#include "lib/sysdep/filesystem.h"
#include "lib/sysdep/os.h"
#include <boost/version.hpp>
#include <cerrno>
#include <cstring>
#include <chrono>
#include <filesystem>
#include <memory>
#include <string>
struct WDIR;
bool DirectoryExists(const OsPath& path)
{
WDIR* dir = wopendir(path);
if(dir)
try
{
wclosedir(dir);
return true;
return std::filesystem::is_directory(path.string());
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("DirectoryExists: failed to check if directory '%s' exists, reason: %s\n", path.string8().c_str(), err.what());
}
return false;
}
@ -55,158 +50,152 @@ bool DirectoryExists(const OsPath& path)
bool FileExists(const OsPath& pathname)
{
struct stat s;
const bool exists = wstat(pathname, &s) == 0;
return exists;
try
{
return std::filesystem::is_regular_file(pathname.string());
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("FileExists: failed to check if file '%s' exists, reason: %s\n", pathname.string8().c_str(), err.what());
}
return false;
}
u64 FileSize(const OsPath& pathname)
{
struct stat s;
ENSURE(wstat(pathname, &s) == 0);
return s.st_size;
try
{
return static_cast<u64>(std::filesystem::file_size(pathname.string()));
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("FileSize: failed to get filesize for '%s', reason: %s\n", pathname.string8().c_str(), err.what());
}
return 0;
}
Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)
{
errno = 0;
struct stat s;
memset(&s, 0, sizeof(s));
if(wstat(pathname, &s) != 0)
WARN_RETURN(StatusFromErrno());
*pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);
try
{
const std::filesystem::path path{pathname.string()};
*pPtrInfo = CFileInfo(path.filename().wstring(), static_cast<u64>(std::filesystem::file_size(path)),
static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(std::filesystem::last_write_time(path).time_since_epoch()).count()));
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("GetFileInfo: failed to get file info for '%s', reason: %s\n", pathname.string8().c_str(), err.what());
return ERR::EXCEPTION;
}
return INFO::OK;
}
struct DirDeleter
{
void operator()(WDIR* osDir) const
{
const int ret = wclosedir(osDir);
ENSURE(ret == 0);
}
};
Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)
{
// open directory
errno = 0;
WDIR* pDir = wopendir(path);
if(!pDir)
return StatusFromErrno(); // NOWARN
std::shared_ptr<WDIR> osDir(pDir, DirDeleter());
for(;;)
try
{
errno = 0;
struct wdirent* osEnt = wreaddir(osDir.get());
if(!osEnt)
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path.string()))
{
// no error, just no more entries to return
if(!errno)
return INFO::OK;
WARN_RETURN(StatusFromErrno());
}
for(size_t i = 0; osEnt->d_name[i] != '\0'; i++)
RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));
const std::wstring_view name{osEnt->d_name};
// get file information (mode, size, mtime)
struct stat s;
#if OS_WIN
// .. return wdirent directly (much faster than calling stat).
RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));
#else
// .. call regular stat().
errno = 0;
const OsPath pathname = path / OsPath(osEnt->d_name);
if(wstat(pathname, &s) != 0)
{
if(errno == ENOENT)
if (entry.is_directory() && entry.path().filename() != "." && entry.path().filename() != ".." && subdirectoryNames)
{
// TODO: This should be displayed to the user as a LOGWARNING when this code is
// moved to ps/
debug_printf("The path \"%s\" cannot be found. It is probably a dangling link "
"pointing to a non-existent path.\n", pathname.string8().c_str());
continue;
subdirectoryNames->emplace_back(entry.path().filename());
}
else if (entry.is_regular_file() && files)
{
files->emplace_back(entry.path().filename().wstring(), static_cast<u64>(entry.file_size()),
static_cast<time_t>(std::chrono::duration_cast<std::chrono::seconds>(entry.last_write_time().time_since_epoch()).count()));
}
WARN_RETURN(StatusFromErrno());
}
#endif
if(files && S_ISREG(s.st_mode))
files->emplace_back(osEnt->d_name, s.st_size, s.st_mtime);
else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..")
subdirectoryNames->emplace_back(osEnt->d_name);
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("GetDirectoryEntries: failed to get directory entries for'%s', reason: %s\n", path.string8().c_str(), err.what());
return ERR::EXCEPTION;
}
return INFO::OK;
}
namespace
{
std::filesystem::perms ModeTToPerms(mode_t mode)
{
using std::filesystem::perms;
perms perm{perms::none};
if (mode | S_IRUSR)
perm |= perms::owner_read;
if (mode | S_IWUSR)
perm |= perms::owner_write;
if (mode | S_IXUSR)
perm |= perms::owner_exec;
if (mode | S_IRGRP)
perm |= perms::group_read;
if (mode | S_IWGRP)
perm |= perms::group_write;
if (mode | S_IXGRP)
perm |= perms::group_exec;
if (mode | S_IROTH)
perm |= perms::others_read;
if (mode | S_IWOTH)
perm |= perms::others_write;
if (mode | S_IXOTH)
perm |= perms::others_exec;
return perm;
}
Status CreateDirectoriesImpl(const std::filesystem::path& path, const std::filesystem::perms& perms)
{
if (std::filesystem::exists(path))
return ERR::FAIL;
if (!std::filesystem::is_directory(path.parent_path()))
{
const Status status = CreateDirectoriesImpl(path.parent_path(), perms);
if (status != INFO::OK)
return status;
}
std::filesystem::create_directory(path);
std::filesystem::permissions(path, perms);
return INFO::OK;
}
} // namespace
Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)
{
if(path.empty())
return INFO::OK;
struct stat s;
if(wstat(path, &s) == 0)
try
{
if(!S_ISDIR(s.st_mode)) // encountered a file
WARN_RETURN(ERR::FAIL);
return INFO::OK;
return CreateDirectoriesImpl(std::filesystem::path(path.string()), ModeTToPerms(mode));
}
// If we were passed a path ending with '/', strip the '/' now so that
// we can consistently use Parent to find parent directory names
if(path.IsDirectory())
return CreateDirectories(path.Parent(), mode, breakpoint);
RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));
errno = 0;
if(wmkdir(path, mode) != 0)
catch (std::filesystem::filesystem_error& err)
{
debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode);
debug_printf("CreateDirectories: failed to create directories '%s', reason: %s\n", path.string8().c_str(), err.what());
if (breakpoint)
WARN_RETURN(StatusFromErrno());
else
return StatusFromErrno();
WARN_RETURN(ERR::EXCEPTION);
else
return ERR::EXCEPTION;
}
return INFO::OK;
}
Status DeleteDirectory(const OsPath& path)
{
// note: we have to recursively empty the directory before it can
// be deleted (required by Windows and POSIX rmdir()).
CFileInfos files; DirectoryNames subdirectoryNames;
RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
// delete files
for(size_t i = 0; i < files.size(); i++)
try
{
const OsPath pathname = path / files[i].Name();
errno = 0;
if(wunlink(pathname) != 0)
WARN_RETURN(StatusFromErrno());
std::filesystem::remove_all(path.string());
}
catch (std::filesystem::filesystem_error& err)
{
debug_printf("DeleteDirectory: failed to delete directory '%s', reason: %s\n", path.string8().c_str(), err.what());
return ERR::EXCEPTION;
}
// recurse over subdirectoryNames
for(size_t i = 0; i < subdirectoryNames.size(); i++)
RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));
errno = 0;
if(wrmdir(path) != 0)
WARN_RETURN(StatusFromErrno());
return INFO::OK;
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -31,32 +31,6 @@
#include "lib/posix/posix_filesystem.h" // mode_t
//
// dirent.h
//
struct WDIR;
struct wdirent
{
// note: SUSv3 describes this as a "char array" but of unspecified size.
// we declare as a pointer to avoid having to copy the string.
wchar_t* d_name;
};
extern WDIR* wopendir(const OsPath& path);
extern wdirent* wreaddir(WDIR*);
// return status for the file returned by the last successful
// wreaddir call from the given directory stream.
// currently sets st_size, st_mode, and st_mtime; the rest are zeroed.
// non-portable, but considerably faster than stat(). used by dir_ForEachSortedEntry.
extern int wreaddir_stat_np(WDIR*, struct stat*);
extern int wclosedir(WDIR*);
//
// fcntl.h
//

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -42,13 +42,6 @@
#include <fcntl.h>
#include <string>
struct WDIR
{
DIR* d;
wchar_t name[PATH_MAX];
wdirent ent;
};
#if OS_ANDROID
// The Crystax NDK seems to do weird things with opendir etc.
@ -84,36 +77,6 @@ void init_libc() { }
#endif
WDIR* wopendir(const OsPath& path)
{
init_libc();
DIR* d = opendir(OsString(path).c_str());
if(!d)
return 0;
WDIR* wd = new WDIR;
wd->d = d;
wd->name[0] = '\0';
wd->ent.d_name = wd->name;
return wd;
}
struct wdirent* wreaddir(WDIR* wd)
{
dirent* ent = readdir(wd->d);
if(!ent)
return 0;
wcscpy_s(wd->name, ARRAY_SIZE(wd->name), OsPath(ent->d_name).string().c_str());
return &wd->ent;
}
int wclosedir(WDIR* wd)
{
int ret = closedir(wd->d);
delete wd;
return ret;
}
int wopen(const OsPath& pathname, int oflag)
{
ENSURE(!(oflag & O_CREAT));

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -30,55 +30,6 @@
#include <atomic>
//-----------------------------------------------------------------------------
// WDIR suballocator
//-----------------------------------------------------------------------------
// most applications only need a single WDIR at a time. we avoid expensive
// heap allocations by reusing a single static instance. if it is already
// in use, we allocate further instances dynamically.
// NB: this is thread-safe due to CAS.
struct WDIR // POD
{
HANDLE hFind;
WIN32_FIND_DATAW findData; // indeterminate if hFind == INVALID_HANDLE_VALUE
// wreaddir will return the address of this member.
// (must be stored in WDIR to allow multiple independent
// wopendir/wreaddir sequences).
struct wdirent ent;
// used by wreaddir to skip the first FindNextFileW. (a counter is
// easy to test/update and also provides useful information.)
size_t numCalls;
};
static WDIR wdir_storage;
static std::atomic<bool> wdir_in_use{ false };
static inline WDIR* wdir_alloc()
{
if(!wdir_in_use.exchange(true)) // gained ownership
return &wdir_storage;
// already in use (rare) - allocate from heap
return new WDIR;
}
static inline void wdir_free(WDIR* d)
{
if(d == &wdir_storage)
{
const bool ok = wdir_in_use.exchange(false); // relinquish ownership
ENSURE(ok); // ensure it wasn't double-freed
}
else // allocated from heap
delete d;
}
//-----------------------------------------------------------------------------
// dirent.h
//-----------------------------------------------------------------------------
@ -127,112 +78,6 @@ static bool IsValidDirectory(const OsPath& path)
return true;
}
// Return owning pointer or nullptr on error.
[[nodiscard]] WDIR* wopendir(const OsPath& path)
{
WinScopedPreserveLastError s;
if(!IsValidDirectory(path))
{
errno = ENOENT;
return 0;
}
WDIR* d = wdir_alloc();
d->numCalls = 0;
// NB: "c:\\path" only returns information about that directory;
// trailing slashes aren't allowed. append "\\*" to retrieve its entries.
OsPath searchPath = path/"*";
// (we don't defer FindFirstFileW until wreaddir because callers
// expect us to return 0 if directory reading will/did fail.)
d->hFind = FindFirstFileW(OsString(searchPath).c_str(), &d->findData);
if(d->hFind != INVALID_HANDLE_VALUE)
return d; // success
const DWORD nativeError{GetLastError()};
if(nativeError == ERROR_NO_MORE_FILES)
return d; // success, but directory is empty
Status status = StatusFromWin();
// release the WDIR allocated above (this is preferable to
// always copying the large WDIR or findData from a temporary)
wdir_free(d);
if(nativeError == ERROR_PATH_NOT_FOUND)
// TODO: This should be displayed to the user as a LOGWARNING when this code is moved to ps/
debug_printf("The path \"%s\" cannot be found. It is probably a dangling link "
"pointing to a non-existent path.\n", path.string8().c_str());
else
WARN_IF_ERR(status);
errno = ErrnoFromStatus(status);
return 0;
}
struct wdirent* wreaddir(WDIR* d)
{
// directory is empty and d->findData is indeterminate
if(d->hFind == INVALID_HANDLE_VALUE)
return 0;
WinScopedPreserveLastError s;
// until end of directory or a valid entry was found:
for(;;)
{
if(d->numCalls++ != 0) // (skip first call to FindNextFileW - see wopendir)
{
if(!FindNextFileW(d->hFind, &d->findData))
{
if(GetLastError() == ERROR_NO_MORE_FILES)
SetLastError(0);
else // unexpected error
DEBUG_WARN_ERR(StatusFromWin());
return 0; // end of directory or error
}
}
// only accept non-hidden and non-system entries - otherwise,
// callers might encounter errors when attempting to open them.
if((d->findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) == 0)
{
d->ent.d_name = d->findData.cFileName; // (NB: d_name is a pointer)
return &d->ent;
}
}
}
int wreaddir_stat_np(WDIR* d, struct stat* s)
{
// NTFS stores UTC but FAT stores local times, which are incorrectly
// translated to UTC based on the _current_ DST settings. we no longer
// bother checking the filesystem, since that's either unreliable or
// expensive. timestamps may therefore be off after a DST transition,
// which means our cached files would be regenerated.
FILETIME* filetime = &d->findData.ftLastWriteTime;
memset(s, 0, sizeof(*s));
s->st_size = (off_t)u64_from_u32(d->findData.nFileSizeHigh, d->findData.nFileSizeLow);
s->st_mode = (unsigned short)((d->findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG);
s->st_mtime = wtime_utc_filetime_to_time_t(filetime);
return 0;
}
int wclosedir(WDIR* d)
{
FindClose(d->hFind);
wdir_free(d);
return 0;
}
//-----------------------------------------------------------------------------
// fcntl.h

View file

@ -45,7 +45,6 @@
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/clntdata.h>
#include <wx/collpane.h>
#include <wx/debug.h>
#include <wx/gdicmn.h>
#include <wx/log.h>
@ -569,19 +568,6 @@ MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContaine
}
}
void MapSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
{
Freeze();
// Toggling the collapsing doesn't seem to update the sidebar layout
// automatically, so do it explicitly here
Layout();
Refresh(); // fixes repaint glitch on Windows
Thaw();
}
void MapSidebar::OnFirstDisplay()
{
// We do this here becase messages are used which requires simulation to be init'd
@ -860,7 +846,6 @@ void MapSidebar::OnResizeMap(wxCommandEvent& WXUNUSED(evt))
}
BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, MapSidebar::OnCollapse)
EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimSlow, MapSidebar::OnSimPlay)
@ -871,4 +856,4 @@ BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
EVT_BUTTON(ID_ResizeMap, MapSidebar::OnResizeMap)
EVT_BUTTON(ID_OpenPlayerPanel, MapSidebar::OnOpenPlayerPanel)
EVT_CHOICE(ID_RandomScript, MapSidebar::OnRandomScript)
END_EVENT_TABLE();
END_EVENT_TABLE()

View file

@ -21,7 +21,6 @@
class MapSettingsControl;
class ScenarioEditor;
class wxCollapsiblePaneEvent;
class wxWindow;
class MapSidebar : public Sidebar
@ -37,7 +36,6 @@ protected:
private:
MapSettingsControl* m_MapSettingsCtrl;
void OnCollapse(wxCollapsiblePaneEvent& evt);
void OnOpenPlayerPanel(wxCommandEvent& evt);
void OnRandomScript(wxCommandEvent& evt);
void OnRandomReseed(wxCommandEvent& evt);

View file

@ -42,7 +42,6 @@
#include <wx/choice.h>
#include <wx/choicebk.h>
#include <wx/clntdata.h>
#include <wx/collpane.h>
#include <wx/colour.h>
#include <wx/colourdata.h>
#include <wx/debug.h>
@ -250,9 +249,6 @@ public:
// TODO: possibly have advanced panel where each player's diplomacy can be set?
// Advanced panel
/*wxCollapsiblePane* advPane = new wxCollapsiblePane(this, wxID_ANY, _("Advanced"));
wxWindow* pane = advPane->GetPane();
diplomacySizer->Add(advPane, 0, wxGROW | wxALL, 2);*/
sizer->Add(diplomacySizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
@ -999,19 +995,6 @@ PlayerSidebar::PlayerSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
scrollSizer->Add(m_PlayerSettingsCtrl, wxSizerFlags().Expand());
}
void PlayerSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
{
Freeze();
// Toggling the collapsing doesn't seem to update the sidebar layout
// automatically, so do it explicitly here
Layout();
Refresh(); // fixes repaint glitch on Windows
Thaw();
}
void PlayerSidebar::OnFirstDisplay()
{
// We do this here becase messages are used which requires simulation to be init'd
@ -1032,7 +1015,3 @@ void PlayerSidebar::OnMapReload()
m_PlayerSettingsCtrl->ReadFromEngine();
}
}
BEGIN_EVENT_TABLE(PlayerSidebar, Sidebar)
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, PlayerSidebar::OnCollapse)
END_EVENT_TABLE();

View file

@ -27,7 +27,6 @@ class PlayerSettingsControl;
class ScenarioEditor;
class wxButton;
class wxChoice;
class wxCollapsiblePaneEvent;
class wxSpinCtrl;
class wxTextCtrl;
class wxWindow;
@ -47,11 +46,7 @@ protected:
private:
PlayerSettingsControl* m_PlayerSettingsCtrl;
void OnCollapse(wxCollapsiblePaneEvent& evt);
bool m_Loaded;
DECLARE_EVENT_TABLE();
};
// Controls present on each player page

View file

@ -48,11 +48,11 @@
#include <wx/chartype.h>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/choicebk.h>
#include <wx/clntdata.h>
#include <wx/colour.h>
#include <wx/gdicmn.h>
#include <wx/image.h>
#include <wx/notebook.h>
#include <wx/object.h>
#include <wx/panel.h>
#include <wx/scrolwin.h>
@ -546,13 +546,14 @@ BEGIN_EVENT_TABLE(TextureNotebookPage, wxPanel)
END_EVENT_TABLE();
class TextureNotebook : public wxNotebook
class TextureNotebook : public wxChoicebook
{
public:
TextureNotebook(ScenarioEditor& scenarioEditor, wxWindow *parent)
: wxNotebook(parent, wxID_ANY/*, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH*/),
: wxChoicebook(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCHB_TOP),
m_ScenarioEditor(scenarioEditor)
{
GetChoiceCtrl()->SetMaxSize(wxSize{300, 100});
}
void LoadTerrain()
@ -607,8 +608,8 @@ private:
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(TextureNotebook, wxNotebook)
EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, TextureNotebook::OnPageChanged)
BEGIN_EVENT_TABLE(TextureNotebook, wxChoicebook)
EVT_CHOICEBOOK_PAGE_CHANGED(wxID_ANY, TextureNotebook::OnPageChanged)
END_EVENT_TABLE();
//////////////////////////////////////////////////////////////////////////