Add menus to Linux character editor

Patch from @x-qq
This commit is contained in:
2020-02-01 19:16:18 -05:00
parent ef734d60d3
commit 5675ab8c71
14 changed files with 339 additions and 64 deletions

View File

@@ -8,10 +8,6 @@
#include <stdexcept>
#include <utility>
// implemented elsewhere
void handle_menu_choice(eMenu);
void handle_menu_spell(eSpell);
OpenBoEMenu::OpenBoEMenu(sf::RenderWindow& window, cUniverse& universe)
: tgui { window }
, mainPtr { window }

View File

@@ -23,6 +23,7 @@ elif str(platform) == "win32":
elif str(platform) == "posix":
pced_sources.extend(Split("""
pc.menus.linux.cpp
pc.menu.cpp
"""))
pced = env.Program("#build/bin/BoE Character Editor", pced_sources + common_sources + party_classes)

View File

@@ -28,13 +28,11 @@ extern rectangle name_rect;
extern rectangle pc_race_rect;
extern rectangle edit_rect[5];
bool handle_action(sf::Event event) {
location the_point;
bool handle_action(sf::Event const & event) {
location the_point = translate_mouse_coordinates({event.mouseButton.x, event.mouseButton.y});
bool to_return = false;
the_point = {event.mouseButton.x, event.mouseButton.y};
if(file_in_mem.empty())
return false;

View File

@@ -2,7 +2,7 @@
#include <SFML/Window/Event.hpp>
#include "dialog.hpp"
bool handle_action(sf::Event event);
bool handle_action(const sf::Event&);
void flash_rect(rectangle to_flash);
void edit_gold_or_food(short which_to_edit);
void display_pc(short pc_num,short mode,cDialog* parent);

View File

@@ -4,6 +4,7 @@
#include "pc.graphics.hpp"
#include "pc.editors.hpp"
#include "pc.action.hpp"
#include "pc.menus.hpp"
#include "sounds.hpp"
#include "gfxsheets.hpp"
#include "render_shapes.hpp"
@@ -18,6 +19,7 @@
extern cUniverse univ;
extern sf::RenderWindow mainPtr;
extern sf::View mainView;
extern bool party_in_scen,scen_items_loaded;
extern fs::path file_in_mem;
@@ -203,6 +205,9 @@ void redraw_screen() {
draw_main_screen();
display_party();
draw_items();
drawMenuBar();
mainPtr.display();
}
@@ -226,7 +231,13 @@ void draw_main_screen() {
rectangle source_rect,dest_rec,dest_rect;
rectangle reg_rect;
tileImage(mainPtr,whole_win_rect,bg[12]); // fill whole window with background texture
// fill whole window with background texture
// Switch back to the default view while drawing the background tiles
// so that they are not upscaled
rectangle windRect { mainPtr };
mainPtr.setView(mainPtr.getDefaultView());
tileImage(mainPtr,windRect,bg[12]);
mainPtr.setView(mainView);
sf::Texture& icon_gworld = *ResMgr::graphics.get("icon");
dest_rec = source_rect = rectangle(icon_gworld);
@@ -792,3 +803,8 @@ void display_party() {
win_draw_string(mainPtr,dest_rect,to_draw.str(),eTextMode::WRAP,style);
}
}
// Translate mouse event coordinates based on the global view and viewport
sf::Vector2f translate_mouse_coordinates(sf::Vector2i const point) {
return mainPtr.mapPixelToCoords(point, mainView);
}

View File

@@ -1,5 +1,7 @@
#include <SFML/Graphics.hpp>
void init_main_buttons();
void Set_up_win ();
void redraw_screen();
void do_button_action(short which_pc,short which_button);
sf::Vector2f translate_mouse_coordinates(sf::Vector2i const point);

View File

@@ -20,6 +20,7 @@
#include "winutil.hpp"
#include "cursors.hpp"
#include "res_image.hpp"
#include "prefs.hpp"
cUniverse univ;
@@ -43,19 +44,21 @@ short current_active_pc = 0;
/* Mac stuff globals */
bool All_Done = false;
sf::Event event;
sf::RenderWindow mainPtr;
sf::View mainView;
fs::path file_in_mem;
bool party_in_scen = false;
bool scen_items_loaded = false;
/* Prototypes */
int main(int argc, char* argv[]);
void Initialize(void);
void Handle_One_Event();
void handle_events();
void handle_one_event(const sf::Event&);
void redraw_everything();
void Handle_Activate();
void Handle_Update();
void Mouse_Pressed();
void Mouse_Pressed(const sf::Event&);
void init_main_window(sf::RenderWindow&, sf::View&);
sf::FloatRect compute_viewport(const sf::RenderWindow&, float ui_scale);
bool verify_restore_quit(std::string dlog);
void set_up_apple_events(int argc, char* argv[]);
extern bool cur_scen_is_mac;
@@ -67,14 +70,22 @@ char start_name[256];
int main(int argc, char* argv[]) {
try {
init_directories(argv[0]);
sync_prefs();
init_main_window(mainPtr, mainView);
init_menubar();
Initialize();
init_fileio();
init_main_buttons();
Set_up_win();
init_shaders();
init_tiling();
init_snd_tool();
#ifdef SFML_SYSTEM_MAC
init_menubar(); // This is called twice because Windows and Mac have different ordering requirements
#endif
check_for_intel();
srand(time(nullptr));
set_up_apple_events(argc, argv);
@@ -83,9 +94,8 @@ int main(int argc, char* argv[]) {
menu_activate();
update_item_menu();
while(!All_Done)
Handle_One_Event();
return 0;
handle_events();
} catch(std::exception& x) {
showFatalError(x.what());
throw;
@@ -96,43 +106,89 @@ int main(int argc, char* argv[]) {
showFatalError("An unknown error occurred!");
throw;
}
return 0;
}
void Initialize(void) {
sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, float ui_scale) {
// See compute_viewport() in boe.graphics.cpp
int const os_specific_y_offset =
#if defined(SFML_SYSTEM_WINDOWS) || defined(SFML_SYSTEM_MAC)
0;
#else
getMenubarHeight();
#endif
sf::FloatRect viewport;
check_for_intel();
viewport.top = float(os_specific_y_offset) / mainPtr.getSize().y;
viewport.left = 0;
viewport.width = ui_scale;
viewport.height = ui_scale;
//
// To make the Random sequences truly random, we need to make the seed start
// at a different number. An easy way to do this is to put the current time
// and date into the seed. Since it is always incrementing the starting seed
// will always be different. Dont for each call of Random, or the sequence
// will no longer be random. Only needed once, here in the init.
srand(time(nullptr));
return viewport;
}
void init_main_window (sf::RenderWindow& mainPtr, sf::View& mainView) {
float ui_scale = get_float_pref("UIScale", 1.0);
// Make a new window for drawing in, and it must be a color window.
// The window is full screen size, made smaller to make it more visible.
int height = 440 + getMenubarHeight();
mainPtr.create(sf::VideoMode(590, height), "Blades of Exile Character Editor", sf::Style::Titlebar | sf::Style::Close);
#ifndef __APPLE__ // This overrides Dock icon on OSX, which isn't what we want at all
const ImageRsrc& icon = ResMgr::graphics.get("icon");
int const width = ui_scale * 590;
int const height = ui_scale * 440
#ifndef SFML_SYSTEM_WINDOWS
+ getMenubarHeight()
#endif
;
mainPtr.create(sf::VideoMode(width, height), "Blades of Exile Character Editor", sf::Style::Titlebar | sf::Style::Close);
mainPtr.setPosition({0,0});
// Initialize the view
mainView.setSize(width, height);
mainView.setCenter(width / 2, height / 2);
// Apply the viewport to the view
sf::FloatRect mainPort = compute_viewport(mainPtr, ui_scale);
mainView.setViewport(mainPort);
// Apply view to the main window
mainPtr.setView(mainView);
#ifndef SFML_SYSTEM_MAC // This overrides Dock icon on OSX, which isn't what we want at all
const ImageRsrc& icon = ResMgr::graphics.get("icon", true);
mainPtr.setIcon(icon->getSize().x, icon->getSize().y, icon->copyToImage().getPixelsPtr());
#endif
init_menubar();
}
void Handle_One_Event() {
if(!mainPtr.pollEvent(event)) return;
void handle_events() {
sf::Event currentEvent;
sf::Clock framerate_clock;
const sf::Int64 desired_microseconds_per_frame { 1000000 / 60 }; // us / FPS
while(!All_Done) {
while(mainPtr.pollEvent(currentEvent)) handle_one_event(currentEvent);
redraw_everything();
// Prevent the loop from executing too fast.
const sf::Int64 remaining_time_budget = desired_microseconds_per_frame - framerate_clock.getElapsedTime().asMicroseconds();
if(remaining_time_budget > 0) sf::sleep(sf::microseconds(remaining_time_budget));
framerate_clock.restart();
}
}
void handle_one_event (const sf::Event& event) {
init_main_buttons();
redraw_screen();
// Check if the menubar wants to handle this event.
if(menuBarProcessEvent(event)) return;
switch(event.type){
case sf::Event::KeyPressed:
break;
case sf::Event::MouseButtonPressed:
Mouse_Pressed();
Mouse_Pressed(event);
break;
case sf::Event::GainedFocus:
@@ -148,7 +204,11 @@ void Handle_One_Event() {
}
}
void Mouse_Pressed() {
void redraw_everything() {
redraw_screen();
}
void Mouse_Pressed(const sf::Event& event) {
bool try_to_end;
try_to_end = handle_action(event);

158
src/pcedit/pc.menu.cpp Normal file
View File

@@ -0,0 +1,158 @@
// Author: xq, Sunday 2020-02-02
#include "pc.menu.hpp"
#include "pc.menus.hpp"
OpenBoEPCEditMenu::OpenBoEPCEditMenu(sf::RenderWindow& window)
: mainPtr { window }
, tgui { window } {
// Build a menubar and store it in tgui with a known name
this->tgui.add(this->build_menubar(), this->internal_menubar_widget_name);
}
tgui::MenuBar::Ptr OpenBoEPCEditMenu::build_menubar() const {
auto menubar = tgui::MenuBar::create();
// XXX TODO FIXME can we get this constant magic number from somewhere?
menubar->setSize(this->mainPtr.getSize().x, 20);
this->add_menu_placeholders(menubar);
this->add_persistent_menu_items(menubar);
return menubar;
}
// This method ensures that the menus on the menubar are in specific order
void OpenBoEPCEditMenu::add_menu_placeholders(tgui::MenuBar::Ptr& menubar) const {
menubar->addMenu("File");
menubar->addMenu("Party");
menubar->addMenu("Scenario");
menubar->addMenu("Help");
}
// This method fills the menu with items that never change.
void OpenBoEPCEditMenu::add_persistent_menu_items(tgui::MenuBar::Ptr& menubar) const {
const std::vector<std::pair <OpenBoEPCEditMenu::MenuHierarchy, eMenu>> persistent_menu_items {
{ { "File", "Open Game Ctrl-O" }, eMenu::FILE_OPEN },
{ { "File", "Close" }, eMenu::FILE_CLOSE },
{ { "File", "Save Game Ctrl-S" }, eMenu::FILE_SAVE },
{ { "File", "Save As..." }, eMenu::FILE_SAVE_AS },
{ { "File", "Revert to Saved" }, eMenu::FILE_REVERT },
{ { "File", "Quit Ctrl-Q" }, eMenu::QUIT },
{ { "Party", "Edit Gold" }, eMenu::EDIT_GOLD },
{ { "Party", "Edit Food" }, eMenu::EDIT_FOOD },
{ { "Party", "Edit Alchemy" }, eMenu::EDIT_ALCHEMY },
{ { "Party", "Heal Damage" }, eMenu::HEAL_DAMAGE },
{ { "Party", "Restore Spell Points" }, eMenu::RESTORE_MANA },
{ { "Party", "Raise Dead, Destone, etc." }, eMenu::RAISE_DEAD },
{ { "Party", "Removed Bad Conditions" }, eMenu::CURE_CONDITIONS },
{ { "Party", "Edit Mage Spells" }, eMenu::EDIT_MAGE },
{ { "Party", "Edit Priest Spells" }, eMenu::EDIT_PRIEST },
{ { "Party", "Edit Traits" }, eMenu::EDIT_TRAITS },
{ { "Party", "Edit Skills" }, eMenu::EDIT_SKILLS },
{ { "Party", "Edit XP" }, eMenu::EDIT_XP },
{ { "Party", "Reunite Party" }, eMenu::REUNITE_PARTY },
{ { "Scenario", "Edit Day" }, eMenu::EDIT_DAY },
{ { "Scenario", "Leave Town" }, eMenu::LEAVE_TOWN },
{ { "Scenario", "Make Towns Forget You" }, eMenu::RESET_TOWNS },
{ { "Scenario", "Add All Town Maps" }, eMenu::ADD_TOWN_MAPS },
{ { "Scenario", "Add All Outdoor Maps" }, eMenu::ADD_OUT_MAPS },
{ { "Scenario", "Own All Boats/Horses" }, eMenu::OWN_VEHICLES },
{ { "Scenario", "Remove Party From Scenario" }, eMenu::LEAVE_SCENARIO },
{ { "Scenario", "Set Stuff Done Flag" }, eMenu::SET_SDF },
{ { "Help", "About Blades of Exile Editor" }, eMenu::ABOUT },
{ { "Help", "Blades of Exile Editor Help" }, eMenu::HELP_TOC },
};
// Note that signal connection ids are discarded.
for(const auto& item : persistent_menu_items) {
menubar->addMenuItem(item.first);
menubar->connectMenuItem(item.first, handle_menu_choice, item.second);
}
}
bool OpenBoEPCEditMenu::handle_event(const sf::Event& event) {
if(event.type == sf::Event::KeyPressed && this->handle_keypressed_event(event))
return true;
return this->tgui.handleEvent(event);
}
// Returns true if event was consumed
bool OpenBoEPCEditMenu::handle_keypressed_event(const sf::Event& event) {
// NOTE: menu items get dynamically enabled/disabled based
// on gamestate, but these keyboard shortcuts do not. So
// this may not be the best way to implement them.
// NOTE: since we are manually adding keyboard shortcut descriptions
// to the menu items, they become parts of menu hierarchies
bool event_was_consumed { false };
if(this->is_control_key_pressed()) {
switch(event.key.code) {
case sf::Keyboard::O:
handle_menu_choice(eMenu::FILE_OPEN);
event_was_consumed = true;
break;
case sf::Keyboard::S:
handle_menu_choice(eMenu::FILE_SAVE);
event_was_consumed = true;
break;
case sf::Keyboard::Q:
handle_menu_choice(eMenu::QUIT);
event_was_consumed = true;
break;
default: break;
}
}
return event_was_consumed;
}
bool OpenBoEPCEditMenu::is_control_key_pressed() const {
// NOTE: Control is not cross-platform (apple)
return (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)
|| sf::Keyboard::isKeyPressed(sf::Keyboard::RControl));
}
void OpenBoEPCEditMenu::draw() {
this->tgui.draw();
}
tgui::MenuBar::Ptr OpenBoEPCEditMenu::get_menubar_ptr() const {
return this->tgui.get<tgui::MenuBar>(this->internal_menubar_widget_name);
}
void OpenBoEPCEditMenu::update_for_editor_state(bool party_in_memory, bool party_in_scenario) {
auto menubar = this->get_menubar_ptr();
if(party_in_memory) {
menubar->setMenuEnabled("Party" , true);
menubar->setMenuEnabled("Scenario", party_in_scenario);
menubar->setMenuItemEnabled({ "File", "Close" }, true);
menubar->setMenuItemEnabled({ "File", "Save Game Ctrl-S" }, true);
menubar->setMenuItemEnabled({ "File", "Save As..." }, true);
menubar->setMenuItemEnabled({ "File", "Revert to Saved" }, true);
} else {
menubar->setMenuEnabled("Party" , false);
menubar->setMenuEnabled("Scenario", false);
menubar->setMenuItemEnabled({ "File", "Close" }, false);
menubar->setMenuItemEnabled({ "File", "Save Game Ctrl-S" }, false);
menubar->setMenuItemEnabled({ "File", "Save As..." }, false);
menubar->setMenuItemEnabled({ "File", "Revert to Saved" }, false);
}
}

34
src/pcedit/pc.menu.hpp Normal file
View File

@@ -0,0 +1,34 @@
// Author: xq, Sunday 2020-02-02
#ifndef PC_MENU_HPP
#define PC_MENU_HPP
// NOTE: this also includes SFML for us
#include <TGUI/TGUI.hpp>
#include <vector>
class OpenBoEPCEditMenu {
public:
OpenBoEPCEditMenu(sf::RenderWindow&);
bool handle_event(const sf::Event&);
void draw();
void update_for_editor_state(bool party_in_memory, bool party_in_scenario);
private:
using MenuHierarchy = std::vector<sf::String>;
tgui::Gui tgui;
sf::RenderWindow& mainPtr;
const sf::String internal_menubar_widget_name { "openboe-pcedit-menu" };
tgui::MenuBar::Ptr build_menubar() const;
void add_menu_placeholders(tgui::MenuBar::Ptr&) const;
void add_persistent_menu_items(tgui::MenuBar::Ptr&) const;
tgui::MenuBar::Ptr get_menubar_ptr() const;
bool handle_keypressed_event(const sf::Event&);
bool is_control_key_pressed() const;
};
#endif

View File

@@ -13,6 +13,10 @@ void init_menubar();
void update_item_menu();
void menu_activate();
namespace sf { class Event; };
bool menuBarProcessEvent(const sf::Event&);
void drawMenuBar();
enum class eMenu {
NONE, ABOUT, QUIT,
FILE_OPEN, FILE_CLOSE, FILE_SAVE, FILE_SAVE_AS, FILE_REVERT, HELP_TOC,

View File

@@ -1,42 +1,34 @@
#include "pc.menus.hpp"
#include <map>
#include <SFML/Graphics/RenderWindow.hpp>
#include "universe.hpp"
#include "pc.menu.hpp"
#include "winutil.hpp"
// This is the index of each menu on the menubar
enum {
FILE_MENU_POS = 0,
PARTY_MENU_POS = 1,
SCEN_MENU_POS = 2,
ITEMS_MENU_POS = 3,
HELP_MENU_POS = 7,
};
#include <SFML/Graphics/RenderWindow.hpp>
#include <memory>
extern sf::RenderWindow mainPtr;
extern cUniverse univ;
extern bool scen_items_loaded, party_in_scen;
extern bool party_in_scen;
extern fs::path file_in_mem;
std::map<int,eMenu> menuChoices;
std::shared_ptr<OpenBoEPCEditMenu> menu_ptr;
void init_menubar() {
menu_ptr.reset(new OpenBoEPCEditMenu(mainPtr));
}
void update_item_menu() {
}
void menu_activate() {
menu_ptr->update_for_editor_state(!file_in_mem.empty(), party_in_scen);
}
#include "cursors.hpp"
bool menuBarProcessEvent (sf::Event const & event) {
return menu_ptr->handle_event(event);
}
void drawMenuBar() {
menu_ptr->draw();
}
#include "fileio.hpp"
#include "pc.fileio.hpp"
extern bool party_in_scen, scen_items_loaded;
extern fs::path file_in_mem;
void set_up_apple_events(int argc, char* argv[]) {
}

View File

@@ -124,6 +124,13 @@ void update_item_menu() {
}
}
bool menuBarProcessEvent(const sf::Event&) {
return false;
}
void drawMenuBar() {
}
@implementation MenuHandler
-(void) itemMenu:(id) sender {
ItemWrapper* item = [sender representedObject];

View File

@@ -170,6 +170,13 @@ LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lPara
return CallWindowProc(reinterpret_cast<WNDPROC>(mainProc), handle, message, wParam, lParam);
}
bool menuBarProcessEvent(const sf::Event&) {
return false;
}
void drawMenuBar() {
}
#include "fileio.hpp"
#include "pc.fileio.hpp"

View File

@@ -57,7 +57,7 @@ void Mouse_Pressed(const sf::Event&);
void close_program();
void ding();
void init_main_window(sf::RenderWindow&, sf::View&);
sf::FloatRect compute_viewport(sf::RenderWindow&, float ui_scale);
sf::FloatRect compute_viewport(const sf::RenderWindow&, float ui_scale);
cScenario scenario;
rectangle right_sbar_rect;
@@ -101,7 +101,7 @@ static void init_sbar(std::shared_ptr<cScrollbar>& sbar, rectangle rect, int pgS
sbar->hide();
}
sf::FloatRect compute_viewport(sf::RenderWindow& mainPtr, float ui_scale) {
sf::FloatRect compute_viewport(sf::RenderWindow const & mainPtr, float ui_scale) {
// See compute_viewport() in boe.graphics.cpp
int const os_specific_y_offset =