Expand on text field keyboard shortcuts
- Clipboard support - Text will now wrap when a word is longer than the width of the destination rect (doesn't just apply to text fields, but is most relevant there) - Edit menu stub added in scenario editor code - Rich text keys (eg cut, copy, paste, select all) are no longer processed by the dialog itself; only the text field processes them; this just means that if they were set as button equivalents they would no longer work (you'd have to set the raw equivalent instead, eg ctrl+C instead of copy). - Text fields now rely on SFML's TextEntered event for actual input - the practical result of this is that your keyboard layout is honoured (though non-ASCII characters just display as boxes). - In a similar vein, shift is not auto-applied to the input keys, so you'd have to set shift+2 instead of @ as the key equivalent (this actually fixes some stuff, such as in the spellcasting dialog, since I was already setting shift+2 instead of @).
This commit is contained in:
@@ -32,6 +32,7 @@ extern sf::Texture bg_gworld;
|
||||
extern bool play_sounds;
|
||||
const short cDialog::BG_DARK = 5, cDialog::BG_LIGHT = 16;
|
||||
short cDialog::defaultBackground = cDialog::BG_DARK;
|
||||
cDialog* cDialog::topWindow = nullptr;
|
||||
|
||||
static std::string generateRandomString(){
|
||||
// Not bothering to seed, because it doesn't actually matter if it's truly random.
|
||||
@@ -942,12 +943,21 @@ bool cDialog::remove(std::string key){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDialog::sendInput(cKey key) {
|
||||
if(topWindow == nullptr) return false;
|
||||
std::string field = topWindow->currentFocus;
|
||||
if(field.empty()) return true;
|
||||
dynamic_cast<cTextField*>(topWindow->controls[field])->handleInput(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cDialog::run(){
|
||||
cDialog* formerTop = topWindow;
|
||||
cursor_type former_curs = current_cursor;
|
||||
set_cursor(sword_curs);
|
||||
using kb = sf::Keyboard;
|
||||
kb::Key k;
|
||||
cKey key;
|
||||
cKey key, pendingKey;
|
||||
sf::Event currentEvent;
|
||||
std::string itemHit = "";
|
||||
dialogNotToast = true;
|
||||
@@ -1011,6 +1021,10 @@ void cDialog::run(){
|
||||
key.spec = true;
|
||||
key.k = key_tab;
|
||||
break;
|
||||
case kb::Insert:
|
||||
key.spec = true;
|
||||
key.k = key_insert;
|
||||
break;
|
||||
case kb::F1:
|
||||
key.spec = true;
|
||||
key.k = key_help;
|
||||
@@ -1031,7 +1045,6 @@ void cDialog::run(){
|
||||
key.spec = true;
|
||||
key.k = key_pgdn;
|
||||
break;
|
||||
// TODO: Add cases for key_tab and key_help and others
|
||||
case kb::LShift:
|
||||
case kb::RShift:
|
||||
case kb::LAlt:
|
||||
@@ -1042,54 +1055,40 @@ void cDialog::run(){
|
||||
case kb::RSystem:
|
||||
continue;
|
||||
default:
|
||||
// TODO: Should probably only support system or control depending on OS
|
||||
key.spec = false;
|
||||
if(currentEvent.key.system || currentEvent.key.control) {
|
||||
if(k == kb::C) {
|
||||
key.spec = true;
|
||||
key.k = key_copy;
|
||||
} else if(k == kb::X) {
|
||||
key.spec = true;
|
||||
key.k = key_cut;
|
||||
} else if(k == kb::V) {
|
||||
key.spec = true;
|
||||
key.k = key_paste;
|
||||
} else if(k == kb::A) {
|
||||
key.spec = true;
|
||||
key.k = key_selectall;
|
||||
}
|
||||
if(key.spec) break;
|
||||
}
|
||||
key.c = keyToChar(k, currentEvent.key.shift);
|
||||
key.c = keyToChar(k, false);
|
||||
break;
|
||||
}
|
||||
key.mod = mod_none;
|
||||
if(currentEvent.key.control || currentEvent.key.system) {
|
||||
if(key.spec){
|
||||
if(key.k == key_left) key.k = key_home;
|
||||
else if(key.k == key_right) key.k = key_end;
|
||||
else if(key.k == key_up) key.k = key_pgup;
|
||||
else if(key.k == key_down) key.k = key_pgdn;
|
||||
else if(key.k == key_copy || key.k == key_cut);
|
||||
else if(key.k == key_paste || key.k == key_selectall);
|
||||
else key.mod += mod_ctrl;
|
||||
}else key.mod += mod_ctrl;
|
||||
}
|
||||
if(currentEvent.key.*systemKey)
|
||||
key.mod += mod_ctrl;
|
||||
if(currentEvent.key.shift) key.mod += mod_shift;
|
||||
if(currentEvent.key.alt) key.mod += mod_alt;
|
||||
itemHit = process_keystroke(key); // TODO: This should be a separate check from the fields thing?
|
||||
if(itemHit.empty()) break;
|
||||
itemHit = process_keystroke(key);
|
||||
if(!itemHit.empty())
|
||||
where = controls[itemHit]->getBounds().centre();
|
||||
if(controls[itemHit]->getType() == CTRL_FIELD){
|
||||
// Now check for focused fields.
|
||||
if(currentFocus.empty()) break;
|
||||
// If it's a tab, handle tab order
|
||||
if(key.spec && key.k == key_tab){
|
||||
// Could use key.mod, but this is slightly easier.
|
||||
if(currentEvent.key.shift)
|
||||
handleTabOrder(itemHit, tabOrder.rbegin(), tabOrder.rend());
|
||||
else handleTabOrder(itemHit, tabOrder.begin(), tabOrder.end());
|
||||
} else if(!key.spec || key.k != key_enter || mod_contains(key.mod, mod_alt)) {
|
||||
dynamic_cast<cTextField*>(controls[itemHit])->handleInput(key);
|
||||
itemHit = "";
|
||||
handleTabOrder(currentFocus, tabOrder.rbegin(), tabOrder.rend());
|
||||
else handleTabOrder(currentFocus, tabOrder.begin(), tabOrder.end());
|
||||
} else {
|
||||
// If it's a character key, and the system key (control/command) is not pressed,
|
||||
// we have an upcoming TextEntered event which contains more information.
|
||||
// Otherwise, handle it right away. But never handle enter or escape.
|
||||
if((key.spec && key.k != key_enter && key.k != key_esc) || mod_contains(key.mod, mod_ctrl))
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(key);
|
||||
pendingKey = key;
|
||||
if(key.k != key_enter && key.k != key_esc) itemHit = "";
|
||||
}
|
||||
break;
|
||||
case sf::Event::TextEntered:
|
||||
if(!pendingKey.spec && !currentFocus.empty()) {
|
||||
pendingKey.c = currentEvent.text.unicode;
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(pendingKey);
|
||||
}
|
||||
break;
|
||||
case sf::Event::MouseButtonPressed:
|
||||
@@ -1124,9 +1123,11 @@ void cDialog::run(){
|
||||
itemHit.clear();
|
||||
}
|
||||
win.setVisible(false);
|
||||
// TODO: The introduction of the static topWindow means I may be able to use this instead of parent->win; do I still need parent?
|
||||
sf::RenderWindow* parentWin = &(parent ? parent->win : mainPtr);
|
||||
while(parentWin->pollEvent(currentEvent));
|
||||
set_cursor(former_curs);
|
||||
topWindow = formerTop;
|
||||
}
|
||||
|
||||
template<typename Iter> void cDialog::handleTabOrder(string& itemHit, Iter begin, Iter end) {
|
||||
@@ -1144,8 +1145,6 @@ template<typename Iter> void cDialog::handleTabOrder(string& itemHit, Iter begin
|
||||
if(iter->second->getType() == CTRL_FIELD){
|
||||
if(iter->second->triggerFocusHandler(*this,iter->first,false)){
|
||||
currentFocus = iter->first;
|
||||
} else {
|
||||
itemHit = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1261,10 +1260,6 @@ bool cDialog::addLabelFor(std::string key, std::string label, eLabelPos where, s
|
||||
std::string cDialog::process_keystroke(cKey keyHit){
|
||||
ctrlIter iter = controls.begin();
|
||||
while(iter != controls.end()){
|
||||
if(iter->second->getType() == CTRL_FIELD && iter->second->isVisible() && dynamic_cast<cTextField*>(iter->second)->hasFocus()) {
|
||||
if(!keyHit.spec || (keyHit.k != key_esc && keyHit.k != key_help))
|
||||
return iter->first;
|
||||
}
|
||||
if(iter->second->isVisible() && iter->second->isClickable() && iter->second->getAttachedKey() == keyHit){
|
||||
iter->second->setActive(true);
|
||||
draw();
|
||||
|
@@ -47,6 +47,7 @@ class cDialog {
|
||||
void loadFromFile(std::string path);
|
||||
template<typename Iter> void handleTabOrder(std::string& itemHit, Iter begin, Iter end);
|
||||
std::vector<std::pair<std::string,cTextField*>> tabOrder;
|
||||
static cDialog* topWindow; // Tracks the frontmost dialog.
|
||||
public:
|
||||
/// Performs essential startup initialization. Generally should not be called directly.
|
||||
static void init();
|
||||
@@ -171,6 +172,10 @@ public:
|
||||
/// Get the bounding rect of the dialog.
|
||||
/// @return The dialog's bounding rect.
|
||||
rectangle getBounds() {return winRect;}
|
||||
/// Send keyboard input to the frontmost dialog.
|
||||
/// Currently, only text edit fields will respond to this.
|
||||
/// @return true if there was a dialog opened to send to.
|
||||
static bool sendInput(cKey key);
|
||||
/// Sets whether to animate graphics in dialogs.
|
||||
static bool doAnimations;
|
||||
cDialog& operator=(cDialog& other) = delete;
|
||||
|
@@ -38,11 +38,9 @@ enum eKeyMod {
|
||||
enum eSpecKey {
|
||||
key_left, key_right, key_up, key_down,
|
||||
key_esc, key_enter, key_tab, key_help, // key_help should bind to the help key on Mac and the F1 key on Windows
|
||||
key_bsp, key_del, key_home, key_end, key_pgup, key_pgdn, // TODO: Implement these
|
||||
key_copy, key_cut, key_paste, key_selectall
|
||||
// TODO: On Mac, command-left should trigger key_home; command-right should trigger key_end;
|
||||
// command-up should trigger key_pgup; and command-down should trigger key_pgdn.
|
||||
// This is in addition to the home, end, pgup, pgdn keys triggering these.
|
||||
key_bsp, key_del, key_home, key_end, key_pgup, key_pgdn, key_top, key_bottom, key_insert,
|
||||
key_copy, key_cut, key_paste, key_selectall, key_undo, key_redo,
|
||||
key_word_left, key_word_right, key_word_bsp, key_word_del,
|
||||
};
|
||||
|
||||
/// Represents a keypress.
|
||||
@@ -51,7 +49,7 @@ struct cKey {
|
||||
bool spec;
|
||||
union {
|
||||
/// The character that has been typed.
|
||||
unsigned char c;
|
||||
wchar_t c;
|
||||
/// The special key that was pressed.
|
||||
eSpecKey k;
|
||||
};
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "dialog.h"
|
||||
#include "dlogutil.h"
|
||||
#include "graphtool.h"
|
||||
#include "winutil.h"
|
||||
|
||||
void cTextField::attachClickHandler(click_callback_t) throw(xHandlerNotSupported){
|
||||
throw xHandlerNotSupported(false);
|
||||
@@ -185,10 +186,97 @@ void cTextField::draw(){
|
||||
undo_clip(*inWindow);
|
||||
}
|
||||
|
||||
static cKey divineFunction(cKey key) {
|
||||
/* Summary of platform-dependent navigation/edit keys:
|
||||
Function | Mac | Windows
|
||||
-----------+-------------------+-------------
|
||||
Home | Home, Cmd-Left | Home
|
||||
End | End, Cmd-Right | End
|
||||
PgUp | PgUp, Alt-Up | PgUp
|
||||
PgDn | PgDn, Alt-Down | PgDn
|
||||
To Top | Cmd-Up, Cmd-Home| Ctrl-Up, Ctrl-Home
|
||||
To Bottom | Cmd-Down, Cmd-End | Ctrl-Down, Ctrl-End
|
||||
Word Left | Alt-Left | Ctrl-Left
|
||||
Word Right | Alt-Right | Ctrl-Right
|
||||
Word Del | Alt-Delete | Ctrl-Delete
|
||||
Word Back | Alt-Backspace | Ctrl-Backspace
|
||||
-----------+-------------------+----------------------
|
||||
Cut | Cmd-X, Shift-Del | Ctrl-X, Shift-Del
|
||||
Copy | Cmd-C, Cmd-Ins | Ctrl-C, Ctrl-Ins
|
||||
Paste | Cmd-V, Shift-Ins | Ctrl-V, Shift-Ins
|
||||
Select All | Cmd-A | Ctrl-A
|
||||
Undo | Cmd-Z | Ctrl-Z
|
||||
Redo | Cmd-Y, Shift-Cmd-Z| Ctrl-Y, Shift-Ctrl-Z
|
||||
This is done to more closely emulate native Mac behaviour.
|
||||
The Insert and Shift-Delete combos are included to more closely emulate
|
||||
native Windows behaviour.
|
||||
*/
|
||||
if(!key.spec) {
|
||||
if(mod_contains(key.mod, mod_ctrl)) {
|
||||
if(key.c == 'c') {
|
||||
key.spec = true;
|
||||
key.k = key_copy;
|
||||
} else if(key.c == 'x') {
|
||||
key.spec = true;
|
||||
key.k = key_cut;
|
||||
} else if(key.c == 'v') {
|
||||
key.spec = true;
|
||||
key.k = key_paste;
|
||||
} else if(key.c == 'a') {
|
||||
key.spec = true;
|
||||
key.k = key_selectall;
|
||||
} else if(key.c == 'z') {
|
||||
key.spec = true;
|
||||
if(mod_contains(key.mod, mod_shift)) {
|
||||
key.k = key_redo;
|
||||
key.mod -= mod_shift;
|
||||
} else key.k = key_undo;
|
||||
} else if(key.c == 'y') {
|
||||
key.spec = true;
|
||||
key.k = key_redo;
|
||||
}
|
||||
}
|
||||
if(key.spec) key.mod -= mod_ctrl;
|
||||
} else {
|
||||
eSpecKey former = key.k;
|
||||
if(mod_contains(key.mod, mod_ctrl)) {
|
||||
#ifdef __APPLE__
|
||||
if(key.k == key_left) key.k = key_home;
|
||||
else if(key.k == key_right) key.k = key_end;
|
||||
#else
|
||||
if(key.k == key_left) key.k = key_word_left;
|
||||
else if(key.k == key_right) key.k = key_word_right;
|
||||
#endif
|
||||
else if(key.k == key_up) key.k = key_top;
|
||||
else if(key.k == key_down) key.k = key_bottom;
|
||||
else if(key.k == key_home) key.k = key_top;
|
||||
else if(key.k == key_end) key.k = key_bottom;
|
||||
else if(key.k == key_insert) key.k = key_copy;
|
||||
#ifndef __APPLE__
|
||||
else if(key.k == key_del) key.k = key_word_del;
|
||||
else if(key.k == key_bsp) key.k = key_word_bsp;
|
||||
#else
|
||||
} else if(mod_contains(key.mod, mod_alt)) {
|
||||
if(key.k == key_up) key.k = key_pgup;
|
||||
else if(key.k == key_down) key.k = key_pgdn;
|
||||
else if(key.k == key_left) key.k = key_word_left;
|
||||
else if(key.k == key_right) key.k = key_word_right;
|
||||
else if(key.k == key_del) key.k = key_word_del;
|
||||
else if(key.k == key_bsp) key.k = key_word_bsp;
|
||||
#endif
|
||||
} else if(mod_contains(key.mod, mod_shift)) {
|
||||
if(key.k == key_insert) key.k = key_paste;
|
||||
else if(key.k == key_del) key.k = key_cut;
|
||||
}
|
||||
if(key.k != former) key.mod -= mod_ctrl;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
void cTextField::handleInput(cKey key) {
|
||||
bool select = mod_contains(key.mod, mod_shift);
|
||||
bool word = mod_contains(key.mod, mod_alt) || mod_contains(key.mod, mod_ctrl);
|
||||
bool haveSelection = insertionPoint != selectionPoint;
|
||||
key = divineFunction(key);
|
||||
size_t new_ip;
|
||||
std::string contents = getText();
|
||||
if(!key.spec) {
|
||||
@@ -199,18 +287,18 @@ void cTextField::handleInput(cKey key) {
|
||||
handleInput(deleteKey);
|
||||
contents = getText();
|
||||
}
|
||||
contents.insert(contents.begin() + insertionPoint, key.c);
|
||||
contents.insert(contents.begin() + insertionPoint, char(key.c));
|
||||
selectionPoint = ++insertionPoint;
|
||||
} else switch(key.k) {
|
||||
// TODO: Implement all the other special keys
|
||||
case key_left:
|
||||
case key_left: case key_word_left:
|
||||
if(haveSelection && !select) {
|
||||
selectionPoint = insertionPoint = std::min(selectionPoint,insertionPoint);
|
||||
break;
|
||||
}
|
||||
new_ip = select ? selectionPoint : insertionPoint;
|
||||
if(new_ip == 0) break;
|
||||
if(word) {
|
||||
if(key.k == key_word_left) {
|
||||
new_ip--;
|
||||
while(new_ip > 0 && contents[new_ip - 1] != ' ')
|
||||
new_ip--;
|
||||
@@ -218,14 +306,14 @@ void cTextField::handleInput(cKey key) {
|
||||
(select ? selectionPoint : insertionPoint) = new_ip;
|
||||
if(!select) selectionPoint = insertionPoint;
|
||||
break;
|
||||
case key_right:
|
||||
case key_right: case key_word_right:
|
||||
if(haveSelection && !select) {
|
||||
selectionPoint = insertionPoint = std::max(selectionPoint,insertionPoint);
|
||||
break;
|
||||
}
|
||||
new_ip = select ? selectionPoint : insertionPoint;
|
||||
if(new_ip == contents.length()) break;
|
||||
if(word) {
|
||||
if(key.k == key_word_right) {
|
||||
new_ip++;
|
||||
while(new_ip < contents.length() && contents[new_ip + 1] != ' ')
|
||||
new_ip++;
|
||||
@@ -236,18 +324,15 @@ void cTextField::handleInput(cKey key) {
|
||||
case key_up:
|
||||
case key_down:
|
||||
break;
|
||||
case key_enter:
|
||||
key.spec = false;
|
||||
key.c = '\n';
|
||||
handleInput(key);
|
||||
break;
|
||||
case key_bsp:
|
||||
case key_bsp: case key_word_bsp:
|
||||
if(haveSelection) {
|
||||
if(key.k == key_word_bsp)
|
||||
handleInput({true, key_word_right, mod_shift});
|
||||
auto begin = contents.begin() + std::min(selectionPoint, insertionPoint);
|
||||
auto end = contents.begin() + std::max(selectionPoint, insertionPoint);
|
||||
auto result = contents.erase(begin, end);
|
||||
selectionPoint = insertionPoint = result - contents.begin();
|
||||
} else if(word) {
|
||||
} else if(key.k == key_word_bsp) {
|
||||
cKey selectKey = key;
|
||||
selectKey.k = key_left;
|
||||
handleInput(selectKey);
|
||||
@@ -259,13 +344,15 @@ void cTextField::handleInput(cKey key) {
|
||||
selectionPoint = --insertionPoint;
|
||||
}
|
||||
break;
|
||||
case key_del:
|
||||
case key_del: case key_word_del:
|
||||
if(haveSelection) {
|
||||
if(key.k == key_word_del)
|
||||
handleInput({true, key_word_left, mod_shift});
|
||||
auto begin = contents.begin() + std::min(selectionPoint, insertionPoint);
|
||||
auto end = contents.begin() + std::max(selectionPoint, insertionPoint);
|
||||
auto result = contents.erase(begin, end);
|
||||
selectionPoint = insertionPoint = result - contents.begin();
|
||||
} else if(word) {
|
||||
} else if(key.k == key_word_del) {
|
||||
cKey selectKey = key;
|
||||
selectKey.k = key_left;
|
||||
handleInput(selectKey);
|
||||
@@ -276,21 +363,52 @@ void cTextField::handleInput(cKey key) {
|
||||
contents.erase(insertionPoint,1);
|
||||
}
|
||||
break;
|
||||
case key_top:
|
||||
if(select) selectionPoint = 0;
|
||||
else insertionPoint = 0;
|
||||
break;
|
||||
case key_bottom:
|
||||
if(select) selectionPoint = contents.length();
|
||||
else insertionPoint = contents.length();
|
||||
break;
|
||||
case key_end:
|
||||
case key_home:
|
||||
case key_pgup:
|
||||
case key_pgdn:
|
||||
break;
|
||||
case key_copy:
|
||||
case key_cut:
|
||||
set_clipboard(contents.substr(std::min(insertionPoint,selectionPoint), abs(insertionPoint - selectionPoint)));
|
||||
if(key.k == key_cut) {
|
||||
cKey deleteKey = key;
|
||||
deleteKey.k = key_bsp;
|
||||
handleInput(deleteKey);
|
||||
contents = getText();
|
||||
}
|
||||
break;
|
||||
case key_paste:
|
||||
if(!get_clipboard().empty()) {
|
||||
cKey deleteKey = {true, key_bsp, mod_none};
|
||||
handleInput(deleteKey);
|
||||
contents = getText();
|
||||
std::string toInsert = get_clipboard();
|
||||
contents.insert(insertionPoint, toInsert);
|
||||
insertionPoint += toInsert.length();
|
||||
selectionPoint = insertionPoint;
|
||||
}
|
||||
break;
|
||||
case key_undo:
|
||||
case key_redo:
|
||||
break;
|
||||
case key_selectall:
|
||||
selectionPoint = 0;
|
||||
insertionPoint = contents.length();
|
||||
break;
|
||||
// These keys have no function in this context.
|
||||
case key_esc:
|
||||
case key_tab:
|
||||
case key_help:
|
||||
case key_insert:
|
||||
break;
|
||||
}
|
||||
setText(contents);
|
||||
|
@@ -58,6 +58,7 @@ void Handle_Update();
|
||||
void handle_menu_choice(long choice);
|
||||
void handle_apple_menu(int item_hit);
|
||||
void handle_file_menu(int item_hit);
|
||||
void handle_edit_menu(int item_hit);
|
||||
void handle_scenario_menu(int item_hit);
|
||||
void handle_town_menu(int item_hit);
|
||||
void handle_outdoor_menu(int item_hit);
|
||||
@@ -269,6 +270,25 @@ void handle_file_menu(int item_hit) {
|
||||
}
|
||||
}
|
||||
|
||||
void handle_edit_menu(int item_hit) {
|
||||
// Currently, this merely passes appropriate input to the frontmost dialog.
|
||||
// TODO: Handle edit menu operations when no dialog is onscreen.
|
||||
cKey key = {true};
|
||||
switch(item_hit) {
|
||||
case 1: key.k = key_undo; break;
|
||||
case 2: key.k = key_redo; break;
|
||||
case 4: key.k = key_cut; break;
|
||||
case 5: key.k = key_copy; break;
|
||||
case 6: key.k = key_paste; break;
|
||||
case 7: key.k = key_del; break;
|
||||
case 8: key.k = key_selectall; break;
|
||||
}
|
||||
if(!cDialog::sendInput(key)) {
|
||||
// Handle non-dialog edit operations here.
|
||||
switch(key.k) {}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_scenario_menu(int item_hit) {
|
||||
short i;
|
||||
fs::path file;
|
||||
|
@@ -318,7 +318,6 @@
|
||||
<reference key="NSMixedImage" ref="909111550"/>
|
||||
</object>
|
||||
</array>
|
||||
<bool key="NSNoAutoenable">YES</bool>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMenuItem" id="741259600">
|
||||
@@ -1069,62 +1068,6 @@
|
||||
</object>
|
||||
<int key="connectionID">370</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">cut:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="1002243476"/>
|
||||
</object>
|
||||
<int key="connectionID">738</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">paste:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="582640805"/>
|
||||
</object>
|
||||
<int key="connectionID">739</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">redo:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="45032972"/>
|
||||
</object>
|
||||
<int key="connectionID">742</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">undo:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="155194070"/>
|
||||
</object>
|
||||
<int key="connectionID">746</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">copy:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="619324772"/>
|
||||
</object>
|
||||
<int key="connectionID">752</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">delete:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="483607956"/>
|
||||
</object>
|
||||
<int key="connectionID">753</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">selectAll:</string>
|
||||
<reference key="source" ref="1014"/>
|
||||
<reference key="destination" ref="280953604"/>
|
||||
</object>
|
||||
<int key="connectionID">755</int>
|
||||
</object>
|
||||
</array>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<array key="orderedObjects">
|
||||
|
@@ -169,6 +169,7 @@ void update_item_menu() {
|
||||
|
||||
void handle_apple_menu(int item_hit);
|
||||
void handle_file_menu(int item_hit);
|
||||
void handle_edit_menu(int item_hit);
|
||||
void handle_scenario_menu(int item_hit);
|
||||
void handle_town_menu(int item_hit);
|
||||
void handle_outdoor_menu(int item_hit);
|
||||
@@ -186,8 +187,11 @@ void handle_monst_menu(int item_hit);
|
||||
}
|
||||
|
||||
// TODO: Implement edit menu (much work to be done here!)
|
||||
// TODO: Fix edit menu being disabled while a modal dialog is onscreen.
|
||||
// This means setting autoenable to false for the Edit menu and finding a
|
||||
// way to make the menuitems work instead of just doing nothing.
|
||||
-(void) editMenu:(id) sender {
|
||||
(void) sender; // Suppress "unused parameter" warning
|
||||
handle_edit_menu([[sender representedObject] intValue]);
|
||||
}
|
||||
|
||||
-(void) scenMenu:(id) sender {
|
||||
|
@@ -325,12 +325,14 @@ void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::stri
|
||||
if(mode == eTextMode::WRAP) {
|
||||
moveTo = location(dest_rect.left + 1 + adjust_x, dest_rect.top + 1 + adjust_y + 9);
|
||||
for(i = 0; text_len(i) != text_len(i + 1) && i < str_len;i++) {
|
||||
int iLen = text_len(i), lineLen = text_len(last_line_break);
|
||||
if(((text_len(i) - text_len(last_line_break) > (dest_rect.width() - 6))
|
||||
&& (last_word_break > last_line_break)) || (str[i] == '|')) {
|
||||
&& (last_word_break >= last_line_break)) || (str[i] == '|')) {
|
||||
if(str[i] == '|') {
|
||||
if(!options.showBreaks) str[i] = ' ';
|
||||
last_word_break = i + 1;
|
||||
}
|
||||
} else if(last_line_break == last_word_break)
|
||||
last_word_break = i;
|
||||
push_snippets(last_line_break, last_word_break, options, iHilite, str, moveTo);
|
||||
moveTo.y += line_height;
|
||||
last_line_break = last_word_break;
|
||||
|
@@ -26,6 +26,12 @@ fs::path nav_put_party(fs::path def = "");
|
||||
fs::path nav_get_scenario();
|
||||
fs::path nav_put_scenario(fs::path def = "");
|
||||
|
||||
// Deal with text snippets in the clipboard.
|
||||
// If the clipboard contains something other than text,
|
||||
// get_clipboard should return an empty string.
|
||||
void set_clipboard(std::string text);
|
||||
std::string get_clipboard();
|
||||
|
||||
void beep();
|
||||
|
||||
class ModalSession {
|
||||
|
@@ -67,6 +67,22 @@ char keyToChar(sf::Keyboard::Key key, bool isShift) {
|
||||
case kb::Return: return '\n';
|
||||
case kb::BackSpace: return '\b';
|
||||
case kb::Delete: return '\x7f';
|
||||
case kb::Numpad0: return '0';
|
||||
case kb::Numpad1: return '1';
|
||||
case kb::Numpad2: return '2';
|
||||
case kb::Numpad3: return '3';
|
||||
case kb::Numpad4: return '4';
|
||||
case kb::Numpad5: return '5';
|
||||
case kb::Numpad6: return '6';
|
||||
case kb::Numpad7: return '7';
|
||||
case kb::Numpad8: return '8';
|
||||
case kb::Numpad9: return '9';
|
||||
// TODO: Should have Equal here, but SFML doesn't distinguish between normal and keybad equal :/
|
||||
// Ditto for the decimal point.
|
||||
case kb::Divide: return '/';
|
||||
case kb::Multiply: return '*';
|
||||
case kb::Subtract: return '-';
|
||||
case kb::Add: return '+';
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
@@ -106,6 +122,25 @@ void ModalSession::pumpEvents() {
|
||||
[[NSApplication sharedApplication] runModalSession: nsHandle];
|
||||
}
|
||||
|
||||
void set_clipboard(std::string text) {
|
||||
NSString* str = [[NSString stringWithUTF8String: text.c_str()] stringByReplacingOccurrencesOfString: @"|" withString: @"\n"];
|
||||
NSArray* contents = [NSArray arrayWithObject: str];
|
||||
NSPasteboard* pb = [NSPasteboard generalPasteboard];
|
||||
[pb clearContents];
|
||||
[pb writeObjects: contents];
|
||||
}
|
||||
|
||||
std::string get_clipboard() {
|
||||
NSPasteboard* pb = [NSPasteboard generalPasteboard];
|
||||
NSDictionary* options = [NSDictionary dictionary];
|
||||
NSArray* types = [NSArray arrayWithObject: [NSString class]];
|
||||
if(![pb canReadObjectForClasses: types options: options])
|
||||
return "";
|
||||
NSArray* contents = [pb readObjectsForClasses: types options: options];
|
||||
NSString* str = [contents objectAtIndex: 0];
|
||||
return [[str stringByReplacingOccurrencesOfString: @"\n" withString: @"|"] cStringUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
void beep() {
|
||||
NSBeep();
|
||||
}
|
||||
|
Reference in New Issue
Block a user