Update to v068r22 release.

byuu says:

Wanted to torture myself, so I implemented the hardest window of all,
the cheat code editor. I had to sacrifice checkboxes inside lists,
unfortunately, but it's a necessary evil. Maybe some day if we can port
checkboxes inside list items to Windows and GTK+, we can add it back. It
should be the only really apparent GUI sacrifice, though. You toggle
cheats by double-clicking them in the list. Easy to do, but not
apparent.

I also added in the focus policy.
This commit is contained in:
Tim Allen 2010-09-22 22:53:23 +10:00
parent 9484d1bc92
commit 8a91c95002
30 changed files with 271 additions and 33 deletions

View File

@ -53,15 +53,15 @@ objects := $(patsubst %,obj/%.o,$(objects))
# targets
build: ui_build $(objects)
ifeq ($(platform),osx)
test -d ../bsnes-$(profile).app || mkdir -p ../bsnes-$(profile).app/Contents/MacOS
$(strip $(cpp) -o ../bsnes-$(profile).app/Contents/MacOS/bsnes-$(profile) $(objects) $(link))
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
else
$(strip $(cpp) -o out/bsnes-$(profile) $(objects) $(link))
$(strip $(cpp) -o out/bsnes $(objects) $(link))
endif
install:
ifeq ($(platform),x)
install -D -m 755 out/bsnes-$(profile) $(DESTDIR)$(prefix)/bin/bsnes-$(profile)
install -D -m 755 out/bsnes $(DESTDIR)$(prefix)/bin/bsnes
install -D -m 644 qt/data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 qt/data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
gconftool-2 --type bool --set /desktop/gnome/interface/menus_have_icons true
@ -69,7 +69,7 @@ endif
uninstall:
ifeq ($(platform),x)
rm $(DESTDIR)$(prefix)/bin/bsnes-$(profile)
rm $(DESTDIR)$(prefix)/bin/bsnes
rm $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
rm $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
endif

View File

@ -1,5 +1,5 @@
static void EditBox_change(EditBox *self) {
if(self->onChange) self->onChange();
if(self->object->locked == false && self->onChange) self->onChange();
}
void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
@ -38,5 +38,7 @@ string EditBox::text() {
}
void EditBox::setText(const char *text) {
object->locked = true;
gtk_text_buffer_set_text(object->textBuffer, text, -1);
object->locked = false;
}

View File

@ -74,7 +74,7 @@ struct Widget : Object {
void setVisible(bool visible = true);
bool enabled();
void setEnabled(bool enabled = true);
bool focused();
virtual bool focused();
void setFocused();
};
@ -82,6 +82,7 @@ struct Window : Widget {
static Window None;
nall::function<bool ()> onClose;
void create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
bool focused();
void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
void setDefaultFont(Font &font);
void setFont(Font &font);

View File

@ -1,5 +1,5 @@
static void TextBox_change(TextBox *self) {
if(self->onChange) self->onChange();
if(self->object->locked == false && self->onChange) self->onChange();
}
void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
@ -21,5 +21,7 @@ string TextBox::text() {
}
void TextBox::setText(const char *text) {
object->locked = true;
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
object->locked = false;
}

View File

@ -1,6 +1,10 @@
static gint Window_close(Window *window) {
if(window->onClose) return !window->onClose();
return false;
if(window->onClose) {
if(window->onClose()) window->setVisible(false);
return true;
}
window->setVisible(false);
return true;
}
void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
@ -35,6 +39,10 @@ void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, con
gtk_widget_realize(object->widget);
}
bool Window::focused() {
return gtk_window_is_active(GTK_WINDOW(object->widget));
}
void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
gtk_window_move(GTK_WINDOW(object->widget), x, y);
gtk_widget_set_size_request(object->formContainer, width, height);

View File

@ -12,7 +12,7 @@ void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns
}
string EditBox::getText() {
unsigned length = SendMessage(widget->window, WM_GETTEXTLENGTH, 0, 0);
unsigned length = GetWindowTextLength(widget->window);
wchar_t buffer[length + 1];
GetWindowText(widget->window, buffer, length + 1);
buffer[length] = 0;
@ -25,7 +25,9 @@ void EditBox::setText(const char *text) {
string output = text;
output.replace("\r", "");
output.replace("\n", "\r\n");
object->locked = true;
SetWindowText(widget->window, utf16_t(output));
object->locked = false;
}
void EditBox::setEditable(bool editable) {

View File

@ -46,8 +46,8 @@ LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpa
}
RECT rc;
GetClientRect(hwnd, &rc);
unsigned length = GetWindowTextLength(hwnd) + 1;
wchar_t text[length];
unsigned length = GetWindowTextLength(hwnd);
wchar_t text[length + 1];
GetWindowText(hwnd, text, length + 1);
text[length] = 0;
DrawText(ps.hdc, text, -1, &rc, DT_CALCRECT | DT_END_ELLIPSIS);

View File

@ -34,6 +34,10 @@ void ListBox::setHeaderVisible(bool headerVisible) {
);
}
void ListBox::reset() {
ListView_DeleteAllItems(widget->window);
}
void ListBox::resizeColumnsToContent() {
for(unsigned i = 0; i < listBox->columns; i++) {
ListView_SetColumnWidth(widget->window, i, LVSCW_AUTOSIZE_USEHEADER);

View File

@ -1,5 +1,6 @@
struct Object::Data {
unsigned id;
bool locked;
};
struct Font::Data {
@ -80,4 +81,5 @@ Object::Object() {
static unsigned guid = 100;
object = new Object::Data;
object->id = guid++;
object->locked = false;
}

View File

@ -9,6 +9,20 @@ void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns
SendMessage(widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : os.os->proportionalFont), 0);
}
void TextBox::setEditable(bool editable) {
SendMessage(widget->window, EM_SETREADONLY, editable == false, (LPARAM)0);
string TextBox::text() {
unsigned length = GetWindowTextLength(widget->window);
wchar_t text[length + 1];
GetWindowText(widget->window, text, length + 1);
text[length] = 0;
return utf8_t(text);
}
void TextBox::setText(const char *text) {
object->locked = true;
SetWindowText(widget->window, utf16_t(text));
object->locked = false;
}
void TextBox::setEditable(bool editable) {
SendMessage(widget->window, EM_SETREADONLY, editable == false, 0);
}

View File

@ -20,7 +20,7 @@ void Widget::setEnabled(bool enabled) {
}
bool Widget::focused() {
return GetDesktopWindow() == widget->window;
return (GetForegroundWindow() == widget->window);
}
void Widget::setFocused() {

View File

@ -201,7 +201,11 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
switch(msg) {
case WM_CLOSE: {
if(window.onClose) return window.onClose();
if(window.onClose) {
if(window.onClose()) window.setVisible(false);
} else {
window.setVisible(false);
}
return TRUE;
}
@ -268,7 +272,7 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
} else if(dynamic_cast<EditBox*>(object_ptr)) {
EditBox &editBox = (EditBox&)*object_ptr;
if(HIWORD(wparam) == EN_CHANGE) {
if(editBox.onChange) editBox.onChange();
if(editBox.object->locked == false && editBox.onChange) editBox.onChange();
}
} else if(dynamic_cast<RadioBox*>(object_ptr)) {
RadioBox &radioBox = (RadioBox&)*object_ptr;
@ -279,7 +283,7 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
} else if(dynamic_cast<TextBox*>(object_ptr)) {
TextBox &textBox = (TextBox&)*object_ptr;
if(HIWORD(wparam) == EN_CHANGE) {
if(textBox.onChange) textBox.onChange();
if(textBox.object->locked == false && textBox.onChange) textBox.onChange();
}
}
}

View File

@ -213,6 +213,8 @@ struct RadioBox : Widget {
struct TextBox : Widget {
nall::function<void ()> onChange;
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
nall::string text();
void setText(const char *text);
void setEditable(bool editable = true);
};

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "068.21";
static const char Version[] = "068.22";
static const unsigned SerializerVersion = 13;
}
}

View File

@ -1,4 +1,4 @@
ui_objects := ui-main ui-general ui-settings ui-utility ui-cartridge
ui_objects := ui-main ui-general ui-settings ui-tools ui-utility ui-cartridge
ui_objects += ruby phoenix
ui_objects += $(if $(call streq,$(platform),win),resource)
@ -54,6 +54,7 @@ objects := $(ui_objects) $(objects)
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/*)
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/general/*)
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/tools/*)
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/settings/*)
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/utility/*)
obj/ui-cartridge.o: $(ui)/cartridge/cartridge.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/cartridge/*)

View File

@ -17,6 +17,7 @@ using namespace phoenix;
#include "config.hpp"
#include "general/general.hpp"
#include "settings/settings.hpp"
#include "tools/tools.hpp"
#include "utility/utility.hpp"
#include "cartridge/cartridge.hpp"
@ -35,15 +36,21 @@ extern Application application;
struct Style {
enum : unsigned {
#if defined(PHOENIX_WINDOWS)
CheckBoxHeight = 15,
ComboBoxHeight = 22,
EditBoxHeight = 22,
LabelHeight = 15,
SliderHeight = 25,
#elif defined(PHOENIX_GTK)
CheckBoxHeight = 15,
ComboBoxHeight = 22,
EditBoxHeight = 22,
LabelHeight = 15,
SliderHeight = 22,
#elif defined(PHOENIX_QT)
CheckBoxHeight = 15,
ComboBoxHeight = 22,
EditBoxHeight = 22,
LabelHeight = 15,
SliderHeight = 22,
#endif

View File

@ -8,11 +8,13 @@ bool Cartridge::loadNormal(const char *filename) {
loadMemory(SNES::memory::cartram, baseName, ".srm");
loadMemory(SNES::memory::cartrtc, baseName, ".rtc");
utility.setTitle(notdir(nall::basename(baseName)));
cheatEditor.load(nall::basename(baseName));
return true;
}
void Cartridge::unload() {
if(SNES::cartridge.loaded() == false) return;
cheatEditor.save(nall::basename(baseName));
saveMemory(SNES::memory::cartram, baseName, ".srm");
saveMemory(SNES::memory::cartrtc, baseName, ".rtc");
SNES::cartridge.unload();

View File

@ -21,4 +21,6 @@ void Configuration::create() {
attach(audio.mute = false, "audio.mute");
attach(input.driver = "", "input.driver");
attach(settings.focusPolicy = 0, "settings.focusPolicy");
}

View File

@ -23,6 +23,10 @@ struct Configuration : public configuration {
string driver;
} input;
struct Settings {
unsigned focusPolicy;
} settings;
void load();
void save();
void create();

View File

@ -11,7 +11,7 @@ void MainWindow::create() {
systemLoadCartridge.create(system, "Load Cartridge ...");
systemSeparator.create(system);
systemQuit.create(system, "Quit");
setMenuVisible(true);
settings.create(*this, "Settings");
settingsSynchronizeVideo.create(settings, "Synchronize Video");
settingsSynchronizeVideo.setChecked(config.video.synchronize);
@ -22,11 +22,15 @@ void MainWindow::create() {
settingsSeparator.create(settings);
settingsVideo.create(settings, "Video Settings ...");
settingsAdvanced.create(settings, "Advanced Settings ...");
tools.create(*this, "Tools");
toolsCheatEditor.create(tools, "Cheat Editor ...");
help.create(*this, "Help");
viewport.create(*this, 0, 0, 595, 448);
utility.setStatus("");
setMenuVisible(true);
setStatusVisible(true);
systemLoadCartridge.onTick = []() {
@ -59,6 +63,10 @@ void MainWindow::create() {
advancedSettingsWindow.setVisible();
};
toolsCheatEditor.onTick = []() {
cheatEditor.setVisible();
};
onClose = []() {
application.quit = true;
return false;

View File

@ -11,6 +11,7 @@ struct MainWindow : Window {
MenuItem settingsVideo;
MenuItem settingsAdvanced;
Menu tools;
MenuItem toolsCheatEditor;
Menu help;
Viewport viewport;

View File

@ -103,6 +103,9 @@ void Interface::input_poll() {
}
int16_t Interface::input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
//ignore input when main window is not active?
if(config.settings.focusPolicy == 1 && mainWindow.focused() == false) return 0;
if(port == 0) {
if(device == SNES::Input::Device::Joypad) {
switch(id) {

View File

@ -32,6 +32,7 @@ void Application::main(int argc, char **argv) {
mainWindow.create();
videoSettingsWindow.create();
advancedSettingsWindow.create();
cheatEditor.create();
mainWindow.setVisible();
while(os.pending()) os.run();
@ -76,6 +77,13 @@ void Application::main(int argc, char **argv) {
while(os.pending()) os.run();
if(SNES::cartridge.loaded()) {
//pause emulator when main window is inactive?
if(config.settings.focusPolicy == 0) {
if(mainWindow.focused() == false) {
usleep(20 * 1000);
continue;
}
}
SNES::system.run();
} else {
usleep(20 * 1000);

View File

@ -15,9 +15,20 @@ void AdvancedSettingsWindow::create() {
audioDriverLabel.create(*this, x + 200, y, 45, Style::ComboBoxHeight, "Audio:");
audioDriverBox.create (*this, x + 245, y, 150, Style::ComboBoxHeight);
inputDriverLabel.create(*this, x + 400, y, 45, Style::ComboBoxHeight, "Input:");
inputDriverBox.create (*this, x + 445, y, 150, Style::ComboBoxHeight); y += Style::ComboBoxHeight;
inputDriverBox.create (*this, x + 445, y, 150, Style::ComboBoxHeight); y += Style::ComboBoxHeight + 5;
setGeometry(0, 0, 605, y + 5);
focusPolicyLabel.create(*this, x, y, 595, Style::LabelHeight, "Focus Policy :."); y += Style::LabelHeight + 5;
focusPolicyLabel.setFont(application.proportionalFontBold);
focusPolicyPause.create(*this, x, y, 195, Style::CheckBoxHeight, "Pause emulator when inactive");
focusPolicyIgnore.create(focusPolicyPause, x + 200, y, 195, Style::CheckBoxHeight, "Ignore input when inactive");
focusPolicyAllow.create(focusPolicyPause, x + 400, y, 195, Style::CheckBoxHeight, "Always allow input"); y += Style::CheckBoxHeight + 5;
if(config.settings.focusPolicy == 0) focusPolicyPause.setChecked();
if(config.settings.focusPolicy == 1) focusPolicyIgnore.setChecked();
if(config.settings.focusPolicy == 2) focusPolicyAllow.setChecked();
setGeometry(0, 0, 605, y);
lstring list;
@ -57,8 +68,7 @@ void AdvancedSettingsWindow::create() {
config.input.driver = list[advancedSettingsWindow.inputDriverBox.selection()];
};
onClose = []() {
advancedSettingsWindow.setVisible(false);
return false;
};
focusPolicyPause.onTick = []() { config.settings.focusPolicy = 0; };
focusPolicyIgnore.onTick = []() { config.settings.focusPolicy = 1; };
focusPolicyAllow.onTick = []() { config.settings.focusPolicy = 2; };
}

View File

@ -6,6 +6,10 @@ struct AdvancedSettingsWindow : Window {
ComboBox audioDriverBox;
Label inputDriverLabel;
ComboBox inputDriverBox;
Label focusPolicyLabel;
RadioBox focusPolicyPause;
RadioBox focusPolicyIgnore;
RadioBox focusPolicyAllow;
void create();
};

View File

@ -33,11 +33,6 @@ void VideoSettingsWindow::create() {
contrastSlider.onChange = brightnessSlider.onChange = gammaSlider.onChange = gammaRampCheck.onTick =
{ &VideoSettingsWindow::adjust, this };
onClose = []() {
videoSettingsWindow.setVisible(false);
return false;
};
}
void VideoSettingsWindow::adjust() {

View File

@ -0,0 +1,129 @@
CheatEditor cheatEditor;
void CheatEditor::load(string filename) {
SNES::cheat.reset();
cheatList.reset();
for(unsigned i = 0; i < 128; i++) {
cheatList.addItem("");
cheatText[i][0] = strunsigned<3, ' '>(i + 1);
cheatText[i][1] = " ";
cheatText[i][2] = "";
cheatText[i][3] = "";
}
string data;
if(data.readfile(string(filename, ".bsv"))) {
lstring list;
list.split("\n", data);
for(unsigned i = 0; i < 128 && i < list.size(); i++) {
lstring part;
part.split("{}", list[i]);
cheatText[i][1] = part[0];
cheatText[i][2] = part[1];
cheatText[i][3] = part[2];
SNES::cheat[i].enabled = (cheatText[i][1] != " ");
SNES::cheat[i] = cheatText[i][2];
}
}
refresh();
}
void CheatEditor::save(string filename) {
bool savesPresent = false;
for(unsigned i = 0; i < 128; i++) {
if(cheatText[i][2] != "" || cheatText[i][3] != "") {
savesPresent = true;
break;
}
}
if(savesPresent == false) {
unlink(string(filename, ".bsv"));
return;
}
file fp;
if(fp.open(string(filename, ".bsv"), file::mode_write)) {
for(unsigned i = 0; i < 128; i++) {
fp.print(string(cheatText[i][1], "{}", cheatText[i][2], "{}", cheatText[i][3], "\n"));
}
fp.close();
}
cheatList.reset();
cheatList.resizeColumnsToContent();
}
void CheatEditor::create() {
application.windows.append(this);
Window::create(0, 0, 256, 256, "Cheat Editor");
setDefaultFont(application.proportionalFont);
unsigned x = 5, y = 5;
cheatList.create(*this, x, y, 500, 250, "Slot\tOn\tCode\tDescription"); y += 255;
cheatList.setHeaderVisible();
codeLabel.create(*this, x, y, 80, Style::EditBoxHeight, "Code(s):");
codeEdit.create (*this, x + 80, y, 420, Style::EditBoxHeight); y += Style::EditBoxHeight + 5;
descLabel.create(*this, x, y, 80, Style::EditBoxHeight, "Description:");
descEdit.create (*this, x + 80, y, 420, Style::EditBoxHeight); y+= Style::EditBoxHeight + 5;
setGeometry(0, 0, 510, y);
synchronize();
cheatList.onActivate = { &CheatEditor::toggle, this };
cheatList.onChange = { &CheatEditor::synchronize, this };
codeEdit.onChange = descEdit.onChange = { &CheatEditor::bind, this };
}
void CheatEditor::synchronize() {
if(auto position = cheatList.selection()) {
codeEdit.setText(cheatText[position()][2]);
descEdit.setText(cheatText[position()][3]);
codeEdit.setEnabled(true);
descEdit.setEnabled(true);
} else {
codeEdit.setText("");
descEdit.setText("");
codeEdit.setEnabled(false);
descEdit.setEnabled(false);
}
}
void CheatEditor::refresh() {
SNES::cheat.synchronize();
for(unsigned i = 0; i < 128; i++) {
lstring list;
list.split("+", cheatText[i][2]);
string cheatCode = list[0];
if(list.size() > 1) cheatCode.append("...");
cheatList.setItem(i, string(
cheatText[i][0], "\t", cheatText[i][1], "\t", cheatCode, "\t", cheatText[i][3]
));
}
cheatList.resizeColumnsToContent();
}
void CheatEditor::toggle() {
if(auto position = cheatList.selection()) {
if(cheatText[position()][1] == " ") {
cheatText[position()][1] = "X";
SNES::cheat[position()].enabled = true;
} else {
cheatText[position()][1] = " ";
SNES::cheat[position()].enabled = false;
}
}
refresh();
}
void CheatEditor::bind() {
if(auto position = cheatList.selection()) {
cheatText[position()][2] = codeEdit.text();
cheatText[position()][3] = descEdit.text();
SNES::cheat[position()] = cheatText[position()][2];
refresh();
}
}

View File

@ -0,0 +1,20 @@
struct CheatEditor : Window {
ListBox cheatList;
Label codeLabel;
TextBox codeEdit;
Label descLabel;
TextBox descEdit;
void load(string filename);
void save(string filename);
void create();
private:
string cheatText[128][4];
void synchronize();
void refresh();
void toggle();
void bind();
};
extern CheatEditor cheatEditor;

View File

@ -0,0 +1,2 @@
#include "../base.hpp"
#include "cheat-editor.cpp"

View File

@ -0,0 +1 @@
#include "cheat-editor.hpp"