[PATCH] User Defined Hot Keys -- In Progress

Programmers discuss here anything related to FreeOrion programming. Primarily for the developers to discuss.

Moderator: Committer

Post Reply
Message
Author
fixIt
Space Floater
Posts: 27
Joined: Thu Oct 20, 2011 10:14 pm

[PATCH] User Defined Hot Keys -- In Progress

#1 Post by fixIt »

Okay so I've gotten a bit swamped with work lately so haven't had much time to work on this patch, but here is what I have finished so far. I just wanted to check in and make sure I'm on track/figure out what I may be missing and still need to do.

Whats implemented so far:
-Added a Hot Keys tab under Options
-This Hot Keys tab has a button for each configurable hot key
-The configurable hot key button opens up a modal window which displays the current set hot key, you can enter a key combination and it is saved(moving the cursor off the window exits out not saving anything)
-The configured hot keys work in game

Things I still need to implement and/or look into:
-Possibly figure out a better way for adding the initial hot key to the DB(the current int/string way seems a bit messy)
-Add the logic behind the use default hot keys button
-State logic for hot keys being enabled/disabled depending on the current window
-Pretty up the UI parts(modal window, options Hot Keys tab/buttons)
-General clean up of the code
-Change the location of the actual HotKeyManger .cpp/.h files?(I created them within visual studio, in the VS filter they are under UI but it looks like they are really in a folder with .asm files)


The few questions I currently have:
Is there a way to change the text size of a GG::TextControl()? I've been looking but can only manage to find a method that tells me the size, i'd like to make the text for the current set Hot Key combination on the modal window bigger.

Is there anything major you see that I am missing from the needs to be implemented list ? Probably the most major thing I can think of is the enabling/disabling depending on the states.

Also how would you like the UI portion of this to look/be changed around? I know its not all that pretty, so far I've just been aiming to get the functionality done.

Also, when the time comes, what is the process to adding a file to the patch that isn't in the repository(HotKeyManager.cpp/.h)? When I create a patch it isn't added.

Here is the HotKeyManager code since I can't seem to get it added to the patch:

Code: Select all

// -*- C++ -*-
//HotKeyManager.h
#ifndef _HotKeyManager_h_
#define _HotKeyManager_h_
#include <../GG/GG/Base.h>
#include <GG/WndEvent.h>
#include <GG/GUI.h>
#include "../../UI/MapWnd.h"


class HotKeyManager;

/////////////////////////////////////////////
// Free Functions
/////////////////////////////////////////////
/** returns the singleton Hot Key Manager */
//HotKeyManager& GetHotKeyManager();



struct HotKeyCombination
{
    HotKeyCombination(){}
    HotKeyCombination(std::string hot_key_name, GG::Key key, GG::Flags<GG::ModKey> default_modkeys, boost::uint32_t key_code_point)
                      : m_hot_key_name(hot_key_name), m_key(key), m_default_modkeys(default_modkeys), m_key_code_point(key_code_point)
    {
    }
    std::string m_hot_key_name;
    GG::Key m_key;
    GG::Flags<GG::ModKey> m_default_modkeys;
    boost::uint32_t m_key_code_point;
};

/** Hot Key Manager */
class HotKeyManager
{
public:

    static HotKeyManager* Instance();
    void SetHotKeyAccelerator(std::string hot_key_name);
    void ChangeHotKey(std::string hot_key_name, GG::Key key, GG::Flags<GG::ModKey> default_modkeys, boost::uint32_t key_code_point);
    GG::GUI::AcceleratorSignalType& GetHotKeySignal(std::string hot_key_name);
    void ConnectKeyboardAcceleratorSignals(MapWnd* map_window);        //!< connects signals from keyboard accelerators to various GUI responses
    void SetAccelerators();                          //!< tells the GUI which keypress combinations to track and emit signals for when they occur
    void RemoveAccelerators();                       //!< tells GUI to stop emitting signals for keypresses
    std::string HotKeyToString(std::string hot_key_name);
    HotKeyCombination ReadHotKeyFromDB(std::string hot_key_name);
    void WriteHotKeyComboToDB(HotKeyCombination hot_key_combo);
    bool WaitForNextKeyPress(GG::Key key);
    std::string SpecialHotKeyToString(HotKeyCombination hot_key_combo, bool only_check_modkeys);
private:
    HotKeyManager();
    HotKeyManager(const HotKeyManager&){};
    HotKeyManager& operator=(const HotKeyManager&){};
    static HotKeyManager* m_sInstance;
    std::set<boost::signals::connection> m_keyboard_accelerator_signals;
    MapWnd* m_map_window;
    bool m_map_window_ptr_set;
};





#endif

Code: Select all

#include "HotKeyManager.h"

#include "../util/OptionsDB.h"
#include <iostream>
#include <boost/algorithm/string.hpp>
#include "../../../FreeOrion/GG/GG/Edit.h"
#include "../util/MultiplayerCommon.h"

HotKeyManager::HotKeyManager() : m_map_window_ptr_set(false)
{

}
/////////////////////////////////////////////////
// HotKeyManager                               //
/////////////////////////////////////////////////
// static(s)
HotKeyManager* HotKeyManager::m_sInstance = nullptr;


HotKeyManager* HotKeyManager::Instance()
{
    if (!m_sInstance)   // Only allow one instance of class to be generated.
      m_sInstance = new HotKeyManager();
   return m_sInstance;
}

void HotKeyManager::SetHotKeyAccelerator(std::string hot_key_name)
{
    HotKeyCombination hot_key_combo = ReadHotKeyFromDB(hot_key_name);
    GG::GUI::GetGUI()->SetAccelerator(hot_key_combo.m_key, hot_key_combo.m_default_modkeys);

}

void HotKeyManager::ChangeHotKey(std::string hot_key_name, GG::Key key, GG::Flags<GG::ModKey> default_modkeys, boost::uint32_t key_code_point)
{
     HotKeyCombination hot_key_combo(hot_key_name, key, default_modkeys, key_code_point);
     WriteHotKeyComboToDB(hot_key_combo);
     RemoveAccelerators();

    // needed for connecting signals
    if (m_map_window_ptr_set)
    {
       SetAccelerators();
       ConnectKeyboardAcceleratorSignals(m_map_window);
    }
}


GG::GUI::AcceleratorSignalType& HotKeyManager::GetHotKeySignal(std::string hot_key_name)
{
    HotKeyCombination hot_key_combo = ReadHotKeyFromDB(hot_key_name);
    return GG::GUI::GetGUI()->AcceleratorSignal(hot_key_combo.m_key, hot_key_combo.m_default_modkeys);
}

void HotKeyManager::ConnectKeyboardAcceleratorSignals(MapWnd* map_window)
{
       m_map_window = map_window;
       m_map_window_ptr_set = true;

       m_keyboard_accelerator_signals.insert(
            GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ReturnToMap"),
                    &MapWnd::ReturnToMap, map_window));

    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.OpenChatWindow1"),
                    &MapWnd::OpenChatWindow, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.OpenChatWindow2"),
                    &MapWnd::OpenChatWindow, map_window));

    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.EndTurn1"),
                    &MapWnd::EndTurn, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.EndTurn2"),
                    &MapWnd::EndTurn, map_window));

    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ToggleSitRep"),
                    &MapWnd::ToggleSitRep, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ToggleResearch"),
                    &MapWnd::ToggleResearch, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ToggleProduction"),
                    &MapWnd::ToggleProduction, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ToggleDesign"),
                    &MapWnd::ToggleDesign, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ShowMenu"),
                    &MapWnd::ShowMenu, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.CloseSystemView"),
                    &MapWnd::CloseSystemView, map_window));

    // Keys for zooming
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.KeyboardZoomIn1"),
                    &MapWnd::KeyboardZoomIn, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.KeyboardZoomIn2"),
                    &MapWnd::KeyboardZoomIn, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.KeyboardZoomOut1"),
                    &MapWnd::KeyboardZoomOut, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.KeyboardZoomOut2"),
                    &MapWnd::KeyboardZoomOut, map_window));

    // Keys for showing systems
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToHomeSystem"),
                    &MapWnd::ZoomToHomeSystem, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToPrevOwnedSystem"),
                    &MapWnd::ZoomToPrevOwnedSystem, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToNextOwnedSystem"),
                    &MapWnd::ZoomToNextOwnedSystem, map_window));

    // Keys for showing fleets
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToPrevIdleFleet"),
                    &MapWnd::ZoomToPrevIdleFleet, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToNextIdleFleet"),
                    &MapWnd::ZoomToNextIdleFleet, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToPrevFleet"),
                    &MapWnd::ZoomToPrevFleet, map_window));
    m_keyboard_accelerator_signals.insert(
        GG::Connect(HotKeyManager::Instance()->GetHotKeySignal("HotKey.ZoomToNextFleet"),
                    &MapWnd::ZoomToNextFleet, map_window));
// set up the regression hotkey
}

void HotKeyManager::SetAccelerators()
{
    HotKeyToString("HotKey.ReturnToMap");
    SetHotKeyAccelerator("HotKey.ReturnToMap");
    SetHotKeyAccelerator("HotKey.OpenChatWindow1");
    SetHotKeyAccelerator("HotKey.OpenChatWindow2");
    SetHotKeyAccelerator("HotKey.EndTurn1");
    SetHotKeyAccelerator("HotKey.EndTurn2");
    SetHotKeyAccelerator("HotKey.ToggleSitRep");
    SetHotKeyAccelerator("HotKey.ToggleResearch");
    SetHotKeyAccelerator("HotKey.ToggleProduction");
    SetHotKeyAccelerator("HotKey.ToggleDesign");
    SetHotKeyAccelerator("HotKey.ShowMenu");
    SetHotKeyAccelerator("HotKey.CloseSystemView");
    SetHotKeyAccelerator("HotKey.KeyboardZoomIn1");
    SetHotKeyAccelerator("HotKey.KeyboardZoomIn2");
    SetHotKeyAccelerator("HotKey.KeyboardZoomOut1");
    SetHotKeyAccelerator("HotKey.KeyboardZoomOut2");
    SetHotKeyAccelerator("HotKey.ZoomToHomeSystem");
    SetHotKeyAccelerator("HotKey.ZoomToPrevOwnedSystem");
    SetHotKeyAccelerator("HotKey.ZoomToNextOwnedSystem");
    SetHotKeyAccelerator("HotKey.ZoomToPrevIdleFleet");
    SetHotKeyAccelerator("HotKey.ZoomToNextIdleFleet");
    SetHotKeyAccelerator("HotKey.ZoomToPrevFleet");
    SetHotKeyAccelerator("HotKey.ZoomToNextFleet");
}


void HotKeyManager::RemoveAccelerators()
{
    GG::GUI::accel_iterator i = GG::GUI::GetGUI()->accel_begin();
    while (i != GG::GUI::GetGUI()->accel_end()) {
        GG::GUI::GetGUI()->RemoveAccelerator(i);
        i = GG::GUI::GetGUI()->accel_begin();
    }


    for (std::set<boost::signals::connection>::iterator it =
             m_keyboard_accelerator_signals.begin();
         it != m_keyboard_accelerator_signals.end();
         ++it) {
        it->disconnect();
    }
    m_keyboard_accelerator_signals.clear();

}



std::string HotKeyManager::HotKeyToString(std::string hot_key_name)
{
    HotKeyCombination hot_key_combo = ReadHotKeyFromDB(hot_key_name);
    //std::string a = boost::lexical_cast<std::string>(hot_key_combo.m_key);

    std::string full_hot_key_string;
    std::string translated_code_point_key;

    GG::GetTranslatedCodePoint(hot_key_combo.m_key, hot_key_combo.m_key_code_point, hot_key_combo.m_default_modkeys, translated_code_point_key);

    if (translated_code_point_key == "")
        full_hot_key_string = SpecialHotKeyToString(hot_key_combo, false);
    else
        full_hot_key_string += SpecialHotKeyToString(hot_key_combo, true) + translated_code_point_key;
    
    std::cout << full_hot_key_string + " ";

    return full_hot_key_string;
}

// arrows
// fkeys
// modkeys
// spacebar
// tab
// enter/return
// backspace
// insert/delete/home/end/pageup/pagedown

std::string HotKeyManager::SpecialHotKeyToString(HotKeyCombination hot_key_combo, bool only_check_modkeys)
{
    std::string hot_key_string;

      // check modkeys
    GG::Flags<GG::ModKey> modkeys = hot_key_combo.m_default_modkeys;
      
    if (modkeys & GG::MOD_KEY_CTRL) {
        hot_key_string += "CTRL ";
    } 
    if(modkeys & GG::MOD_KEY_SHIFT) {
        hot_key_string += "SHIFT ";
    }
    if (modkeys & GG::MOD_KEY_ALT) {
        hot_key_string += "ALT ";
    }
    if (modkeys & GG::MOD_KEY_META) {
        hot_key_string += "META ";
    }
    if (modkeys & GG::MOD_KEY_NUM) {
        hot_key_string += "NUM LOCK ";
    }
    if (modkeys & GG::MOD_KEY_CAPS) {
        hot_key_string += "CAPS LOCK ";
    }
    if (modkeys & GG::MOD_KEY_MODE) {
        hot_key_string += "MODE ";
    }





    if (!only_check_modkeys) {
        switch(hot_key_combo.m_key) {
            case GG::GGK_LEFT:
                hot_key_string += UserString("Left_Arrow");
                break;
            case GG::GGK_RIGHT:
                hot_key_string += UserString("Right_Arrow");
                break;
            case GG::GGK_UP:
                hot_key_string += UserString("Up_Arrow");
                break;
            case GG::GGK_DOWN:
                hot_key_string += UserString("Down_Arrow");
                break;
            case GG::GGK_F1:
                hot_key_string += UserString("F1");
                break;
            case GG::GGK_F2:
                hot_key_string += UserString("F2");
                break;
            case GG::GGK_F3:
                hot_key_string += UserString("F3");
                break;
            case GG::GGK_F4:
                hot_key_string += UserString("F4");
                break;
            case GG::GGK_F5:
                hot_key_string += UserString("F5");
                break;
            case GG::GGK_F6:
                hot_key_string += UserString("F6");
                break;
            case GG::GGK_F7:
                hot_key_string += UserString("F7");
                break;
            case GG::GGK_F8:
                hot_key_string += UserString("F8");
                break;
            case GG::GGK_F9:
                hot_key_string += UserString("F9");
                break;
            case GG::GGK_F10:
                hot_key_string += UserString("F10");
                break;
            case GG::GGK_F11:
                hot_key_string += UserString("F11");
                break;
            case GG::GGK_F12:
                hot_key_string += UserString("F12");
                break;


            case GG::GGK_SPACE:
                hot_key_string += UserString("Space");
                break;
            case GG::GGK_TAB:
                hot_key_string += UserString("Tab");
                break;
            case GG::GGK_KP_ENTER:
                hot_key_string += UserString("Enter");
                break;
            case GG::GGK_RETURN:
                hot_key_string += UserString("Return");
                break;
            case GG::GGK_BACKSPACE:
                hot_key_string += UserString("Backspace");
                break;
                
                // insert/delete/home/end/pageup/pagedown
            case GG::GGK_INSERT:
                hot_key_string += UserString("Insert");
                break;
            case GG::GGK_DELETE:
                hot_key_string += UserString("Delete");
                break;
            case GG::GGK_HOME:
                hot_key_string += UserString("Home");
                break;
            case GG::GGK_END:
                hot_key_string += UserString("End");
                break;
            case GG::GGK_PAGEUP:
                hot_key_string += UserString("Page_Up");
                break;
            case GG::GGK_PAGEDOWN:
                hot_key_string += UserString("Page_Down");
                break;
            default:
                break;
        }
    }


    return hot_key_string;

}




HotKeyCombination HotKeyManager::ReadHotKeyFromDB(std::string hot_key_name)
{
    std::string db_stored_key = GetOptionsDB().Get<std::string>(hot_key_name);

    std::vector<std::string> split_strings;
    boost::split(split_strings, db_stored_key, boost::is_any_of(" "));

    GG::Key key = (GG::Key)boost::lexical_cast<int>(split_strings.at(0));
    GG::Flags<GG::ModKey> modkeys;
    boost::uint32_t key_code_point;

    for (unsigned int x = 1; x < split_strings.size()-1; ++x)
    {
        std::string cur = split_strings.at(x);

        if (cur == "c") {
            modkeys |= GG::MOD_KEY_CTRL;
        } else if(cur == "s") {
            modkeys |= GG::MOD_KEY_SHIFT;
        } else if (cur == "a") {
            modkeys |= GG::MOD_KEY_ALT;
        } else if (cur == "m") {
            modkeys |= GG::MOD_KEY_META;
        } else if(cur == "num") {
            modkeys |= GG::MOD_KEY_NUM;
        } else if (cur == "cap") {
            modkeys |= GG::MOD_KEY_CAPS;
        } else if (cur == "mode") {
            modkeys |= GG::MOD_KEY_MODE;
        }
    }
    
    if ((split_strings.size() - 1) != 0)
        key_code_point = boost::lexical_cast<boost::uint32_t>(split_strings.at(split_strings.size()-1));
    else
        key_code_point = 0;
    
    HotKeyCombination db_key_combo(hot_key_name, key, modkeys, key_code_point);

    return db_key_combo;
}


void HotKeyManager::WriteHotKeyComboToDB(HotKeyCombination hot_key_combo)
{
    // store new keys combo
    int key_int = (int) hot_key_combo.m_key;
    GG::Flags<GG::ModKey> default_modkeys = hot_key_combo.m_default_modkeys;
    std::string hot_key_string = boost::lexical_cast<std::string>(key_int);

    if (default_modkeys & GG::MOD_KEY_CTRL) {
        hot_key_string += " c";
    } 
    if(default_modkeys & GG::MOD_KEY_SHIFT) {
        hot_key_string += " s";
    }
    if (default_modkeys & GG::MOD_KEY_ALT) {
        hot_key_string += " a";
    }
    if (default_modkeys & GG::MOD_KEY_META) {
        hot_key_string += " m";
    }
    if (default_modkeys & GG::MOD_KEY_NUM) {
        hot_key_string += " num";
    }
    if (default_modkeys & GG::MOD_KEY_CAPS) {
        hot_key_string += " cap";
    }

    if (default_modkeys & GG::MOD_KEY_MODE) {
        hot_key_string += " mode";
    }





    hot_key_string += " " + boost::lexical_cast<std::string>(hot_key_combo.m_key_code_point);

    GetOptionsDB().Set<std::string>(hot_key_combo.m_hot_key_name, hot_key_string);

}


// fix gui
// add states
// clean up code
// cancel button

bool HotKeyManager::WaitForNextKeyPress(GG::Key key)
{
    switch(key) {
        case GG::GGK_NUMLOCK:
        case GG::GGK_CAPSLOCK:
        case GG::GGK_RSHIFT:
        case GG::GGK_LSHIFT:
        case GG::GGK_RCTRL:
        case GG::GGK_LCTRL:
        case GG::GGK_RALT:
        case GG::GGK_LALT:
        case GG::GGK_RMETA:
        case GG::GGK_LMETA:
        case GG::GGK_MODE:
            return true;
            break;
        default:
            return false;
            break;
    }
}
Attachments

[The extension patch has been deactivated and can no longer be displayed.]


User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: [PATCH] User Defined Hot Keys -- In Progress

#2 Post by Geoff the Medio »

fixIt wrote:Is there a way to change the text size of a GG::TextControl()?
Do you mean how to create a TextControl with different size of text? You can specify a Font when creating it, of whatever size you desire. If you mean change the font size of an existing TextControl, I don't think that's possible.
Is there anything major you see that I am missing from the needs to be implemented list ? Probably the most major thing I can think of is the enabling/disabling depending on the states.
That's probably the biggest issue, and will be a bit complicated. When the chat window has focus, in particular, is tricky, as not just alphanumeric keys need to be disabled, but most other printable keypresses shouldn't activate hotkeys either.
Also, when the time comes, what is the process to adding a file to the patch that isn't in the repository(HotKeyManager.cpp/.h)? When I create a patch it isn't added.
Just adding a file in the SVN tracked directory doesn't do anything; you need to SVN Add the file. After that, a patch created of the whole directory should include the addition of the new file.

Edit: HotKeyManager.* should be in the UI directory, not in the msv2010 directory.

The format you've used for defining the hotkeys is a bit awkward... Ideally, rather than the option being a string and storing something like "27 0", you could / should store a std::pair<GG::Key, GG::Key> or std::pair<GG::Flags<GG::ModKey>, GG::Key> or similar, and add an appropriate std::ostream& operator<< and std::istream& operator>> (compare struct StreamableColor) so it can be lexical_cast into and from a string. Then, the hard-coded defaults are more readable, as you can use pair(GG::GGK_ESCAPE, GG::GGK_UNKNOWN) or pair(GG::GGK_ESCAPE, GG::Flags<GG::ModKey>()) in the code. If the streaming operators are also defined appropriately, the text stored in config.xml could also be nicer looking, perhaps "escape" and similar readable representations, along with "a", "q", "|" and such for the directly printable keys. This is a "would be nice", rather than an essential requirement, though.

Edit4: You should also be able to simplify things to just

Code: Select all

typedef std::pair<GG::Key, GG::Key> KeyPair;
KeyPair key_pair = GetOptionsDB().Get<KeyPair>("HotKey.OpenChatWindow1");
instead of the present wrapping of stuff inside functions that is inconsistent with how the OptionsDB is used elsewhere.

The HotKeyModalWndButton labels should be looked up in UserString, not hard coded english.

Edit2: Rather than

Code: Select all

        switch(hot_key_combo.m_key) {
            case GG::GGK_LEFT:
                hot_key_string += UserString("Left_Arrow");
                break;
            case GG::GGK_RIGHT:
                hot_key_string += UserString("Right_Arrow");
                break;
            case GG::GGK_UP:
                hot_key_string += UserString("Up_Arrow");
                break;
you could just call UserString once, and use the switch to select what to look up, like

Code: Select all

        string hot_key_string_tag;
        switch(hot_key_combo.m_key) {
            case GG::GGK_LEFT: hot_key_string_tag = "KEY_LEFT_ARROW"; break;
            case GG::GGK_RIGHT: hot_key_string_tag = "KEY_RIGHT_ARROW"; break;
            case GG::GGK_UP: hot_key_string_tag = "KEY_UP_ARROW"; break;
            default: break;
        }
        hot_key_string = UserString(hot_key_string_tag);
That would shorten the whole switch block and make things a bit more readable.

Edit3: In lots of places, you're passing strings or structs by value. Whenever possible, pass by const reference instead.

Edit5: Use C++ casts like static_cast<int> instead of (int) and similar.

Edit6: WaitForNextKeyPress seems oddly named. Should it be IsModKey ?

m_sInstance seems oddly named. If it's static, it's not a member variable (which I think is what m is supposed to indicate; just use s_instance;

ConnectKeyboardAcceleratorSignals has lots of calls to HotKeyManager::Instance(), but doesn't seem to need to, since it's already a member function of HotKeyManager, so could just use s_instance (m_sInstance) directly.

Edit7: They currently bound hotkey for each command should be shown on the options screen. Rather than a button labelled with the command name, have a text label with the command name, and a button or other clickable control to its right, which is labelled with the currently bound hotkey for that command.

Edit8: Rather than a toggle option, have a button to reset all the key bindings.

fixIt
Space Floater
Posts: 27
Joined: Thu Oct 20, 2011 10:14 pm

Re: [PATCH] User Defined Hot Keys -- In Progress

#3 Post by fixIt »

Geoff the Medio wrote:
fixIt wrote:Is there a way to change the text size of a GG::TextControl()?
Do you mean how to create a TextControl with different size of text? You can specify a Font when creating it, of whatever size you desire. If you mean change the font size of an existing TextControl, I don't think that's possible.
Is there anything major you see that I am missing from the needs to be implemented list ? Probably the most major thing I can think of is the enabling/disabling depending on the states.
That's probably the biggest issue, and will be a bit complicated. When the chat window has focus, in particular, is tricky, as not just alphanumeric keys need to be disabled, but most other printable keypresses shouldn't activate hotkeys either.
Also, when the time comes, what is the process to adding a file to the patch that isn't in the repository(HotKeyManager.cpp/.h)? When I create a patch it isn't added.
Just adding a file in the SVN tracked directory doesn't do anything; you need to SVN Add the file. After that, a patch created of the whole directory should include the addition of the new file.

Edit: HotKeyManager.* should be in the UI directory, not in the msv2010 directory.

The format you've used for defining the hotkeys is a bit awkward... Ideally, rather than the option being a string and storing something like "27 0", you could / should store a std::pair<GG::Key, GG::Key> or std::pair<GG::Flags<GG::ModKey>, GG::Key> or similar, and add an appropriate std::ostream& operator<< and std::istream& operator>> (compare struct StreamableColor) so it can be lexical_cast into and from a string. Then, the hard-coded defaults are more readable, as you can use pair(GG::GGK_ESCAPE, GG::GGK_UNKNOWN) or pair(GG::GGK_ESCAPE, GG::Flags<GG::ModKey>()) in the code. If the streaming operators are also defined appropriately, the text stored in config.xml could also be nicer looking, perhaps "escape" and similar readable representations, along with "a", "q", "|" and such for the directly printable keys. This is a "would be nice", rather than an essential requirement, though.

Edit4: You should also be able to simplify things to just

Code: Select all

typedef std::pair<GG::Key, GG::Key> KeyPair;
KeyPair key_pair = GetOptionsDB().Get<KeyPair>("HotKey.OpenChatWindow1");
instead of the present wrapping of stuff inside functions that is inconsistent with how the OptionsDB is used elsewhere.

The HotKeyModalWndButton labels should be looked up in UserString, not hard coded english.

Edit2: Rather than

Code: Select all

        switch(hot_key_combo.m_key) {
            case GG::GGK_LEFT:
                hot_key_string += UserString("Left_Arrow");
                break;
            case GG::GGK_RIGHT:
                hot_key_string += UserString("Right_Arrow");
                break;
            case GG::GGK_UP:
                hot_key_string += UserString("Up_Arrow");
                break;
you could just call UserString once, and use the switch to select what to look up, like

Code: Select all

        string hot_key_string_tag;
        switch(hot_key_combo.m_key) {
            case GG::GGK_LEFT: hot_key_string_tag = "KEY_LEFT_ARROW"; break;
            case GG::GGK_RIGHT: hot_key_string_tag = "KEY_RIGHT_ARROW"; break;
            case GG::GGK_UP: hot_key_string_tag = "KEY_UP_ARROW"; break;
            default: break;
        }
        hot_key_string = UserString(hot_key_string_tag);
That would shorten the whole switch block and make things a bit more readable.

Edit3: In lots of places, you're passing strings or structs by value. Whenever possible, pass by const reference instead.

Edit5: Use C++ casts like static_cast<int> instead of (int) and similar.

Edit6: WaitForNextKeyPress seems oddly named. Should it be IsModKey ?

m_sInstance seems oddly named. If it's static, it's not a member variable (which I think is what m is supposed to indicate; just use s_instance;

ConnectKeyboardAcceleratorSignals has lots of calls to HotKeyManager::Instance(), but doesn't seem to need to, since it's already a member function of HotKeyManager, so could just use s_instance (m_sInstance) directly.

Edit7: They currently bound hotkey for each command should be shown on the options screen. Rather than a button labelled with the command name, have a text label with the command name, and a button or other clickable control to its right, which is labelled with the currently bound hotkey for that command.

Edit8: Rather than a toggle option, have a button to reset all the key bindings.
Okay I apologize I haven't had much free time to work on this patch. I'm still trucking along on it slowly though. Here is what more I have completed since my last post:

-SVN added the HotKeyManager files
-Moved HotKeyManager files to the UI folder
-HotKeyModalWndButton labels are now looked up in UserString
-Shortened the switch statement, used const references where possible, used static_casts where possible as well
-Renamed WaitForNextKeyPress & m_sinstance, removed unnecessary calls to HotKeyManager::Instance()
-Reworked the Hot Key Options UI tab to have a text label which displays what the Hot key controls and a button displaying the currently set key combination
-Changed reset all keys toggle option to a button and implemented the functionality behind it



What still needs to be completed:
-change the way hot keys are initially added/stored into config.xml so pairs are used per the way you suggested above
-implement hot keys being enabled/disabled depending on state
-further clean up and document code

For the enabling/disabling of specific hot keys based on state so far the only one that I am aware of is when the chat window has focus basically no other hot keys should work right? Could you elaborate a bit further on the different scenarios of state/focus and what should and shouldn't be enabled/disabled?

Thanks
Attachments

[The extension patch has been deactivated and can no longer be displayed.]


User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: [PATCH] User Defined Hot Keys -- In Progress

#4 Post by Geoff the Medio »

fixIt wrote:For the enabling/disabling of specific hot keys based on state so far the only one that I am aware of is when the chat window has focus basically no other hot keys should work right? Could you elaborate a bit further on the different scenarios of state/focus and what should and shouldn't be enabled/disabled?
The chatwnd disabling all keys that might be used for typing in chat is the main issue. I suppose hotkeys also don't need to do anything if there is no game currently being played. If/when the combat interface ever gets implemented, entering that would probably disable most map screen hotkeys. So, there should probably be an easy way to list keypresses and commands that should be enabled in various UI states, with others being disabled. For now, I think modes for "not playing game", "playing game", and "typing chat" are needed.

fixIt
Space Floater
Posts: 27
Joined: Thu Oct 20, 2011 10:14 pm

Re: [PATCH] User Defined Hot Keys -- In Progress

#5 Post by fixIt »

Okay sounds good for now I'll begin looking into implementing those 3 modes this weekend.

Geoff the Medio wrote: So, there should probably be an easy way to list keypresses and commands that should be enabled in various UI states, with others being disabled.
By this do you mean you'd like to have a way to say dump all the current activated and deactivated hot key combos to the console? Or do you mean on the actual Hot Key Options tab you'd like to see a 3rd column stating whether its currently activated or deactivated??

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: [PATCH] User Defined Hot Keys -- In Progress

#6 Post by Geoff the Medio »

fixIt wrote:
Geoff the Medio wrote: So, there should probably be an easy way to list keypresses and commands that should be enabled in various UI states, with others being disabled.
By this do you mean you'd like to have a way to say dump all the current activated and deactivated hot key combos to the console? Or do you mean on the actual Hot Key Options tab you'd like to see a 3rd column stating whether its currently activated or deactivated??
This is not about displaying things in the GUI... I meant that in the C++ code, there should be a way to say "create a hotkey configuration state, which will have the following commands and keys available or disabled", and then a way to later tell they hotkey manager to switch to that state (from some other state it was in previously). This would be a convenience, and ensure consistency, rather than having to call DisableHotkeyKeyPress and EnableHotkeyCommand 50 times each when switching between states. You'd still have to call some other functions 50 times when creating the configuration, but that wouldn't be from the code at the time of the state switch.

fixIt
Space Floater
Posts: 27
Joined: Thu Oct 20, 2011 10:14 pm

Re: [PATCH] User Defined Hot Keys -- In Progress

#7 Post by fixIt »

Sorry for the delay on finishing up this patch, been busy getting situated from a move and starting a new summer job. Anyways hopefully what I have added completes this patch speaking in terms of functionality.

What I added since my last patch:
-HotKeys are Activated and Deactivated based on state of game. (When IntroMenu state is entered, when PlayingGame state is entered, when Chat gains and loses focus.)
-HotKeys now also store a "state" in config xml. Specific HotKeys can be activated or deactivated based on a given state. The default state as of now is "Default".

What I plan on doing sometime in the near future:
-Refactor code & more thoroughly comment
-Change the way hot keys are initially added/stored into config.xml so pairs are used per the way you suggested above
-Resolve my code

My one question at the moment is in terms of the ChatWindow, all the boost states involving it only seemed to be when a message was actually sent. Was actually activating/deactivating hot keys in the ChatWnd::gainfocus()/losefocus() the right way to do this, or did I miss the correct boost state to activate/deactivate in?
Attachments

[The extension patch has been deactivated and can no longer be displayed.]


User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: [PATCH] User Defined Hot Keys -- In Progress

#8 Post by Geoff the Medio »

fixIt wrote:...the boost states...
Not sure what "boost states" are that you're referring to... Do you mean the boost::signal that fire when MessageWndEdit gains or loses focus (GainingFocusSignal and LosingFocusSignal) or when the MessageWnd gains or loses focus (DoneTypingSignal and TypingSignal)?

I wonder if those signals all work right, since you could transfer focus from the MessageWndEdit to the message history scroll bar, and still have the MessageWnd itself have focus... or does the MessageWnd not have focus if one of its children has focus?

But yes, if you're talking about what I think you're talking about, then most hotkeys should be disabled whenever the player is typing in the message window, so that typing e or r or some other hotkey actually types the character and doesn't move the map around or whatever action it's normally bound to. Hotkeys probably don't need to be disabled if the chatwnd / messagewnd has focus but the player isn't actually typing (ie. the MessageWndEdit control doesn't have focus).

fixIt
Space Floater
Posts: 27
Joined: Thu Oct 20, 2011 10:14 pm

Re: [PATCH] User Defined Hot Keys -- In Progress

#9 Post by fixIt »

Geoff the Medio wrote:
fixIt wrote:...the boost states...
Not sure what "boost states" are that you're referring to... Do you mean the boost::signal that fire when MessageWndEdit gains or loses focus (GainingFocusSignal and LosingFocusSignal) or when the MessageWnd gains or loses focus (DoneTypingSignal and TypingSignal)?

I wonder if those signals all work right, since you could transfer focus from the MessageWndEdit to the message history scroll bar, and still have the MessageWnd itself have focus... or does the MessageWnd not have focus if one of its children has focus?

But yes, if you're talking about what I think you're talking about, then most hotkeys should be disabled whenever the player is typing in the message window, so that typing e or r or some other hotkey actually types the character and doesn't move the map around or whatever action it's normally bound to. Hotkeys probably don't need to be disabled if the chatwnd / messagewnd has focus but the player isn't actually typing (ie. the MessageWndEdit control doesn't have focus).
I was referring to the boost states in HumanClientFSM, where I am currently deactivating all hot keys when the IntroMenu state is instantiated & activating all hot keys when the PlayingGame state is instantiated. Within HumanClientFSM I saw some sub states involving a Chatwnd/message but they all seemed to be only when a message is actually entered. As of now I call Deactivate/Activate for the chat wnd within MessageWndEdit::gainfocus/losefocus, so I just wasn't sure if I was missing somewhere I could do this in HumanClientFSM.
Geoff the Medio wrote: I wonder if those signals all work right, since you could transfer focus from the MessageWndEdit to the message history scroll bar, and still have the MessageWnd itself have focus... or does the MessageWnd not have focus if one of its children has focus?
I'm not familiar with the history scroll bar, how do you transfer focus from the MessageWndEdit to it?
Geoff the Medio wrote: But yes, if you're talking about what I think you're talking about, then most hotkeys should be disabled whenever the player is typing in the message window, so that typing e or r or some other hotkey actually types the character and doesn't move the map around or whatever action it's normally bound to. Hotkeys probably don't need to be disabled if the chatwnd / messagewnd has focus but the player isn't actually typing (ie. the MessageWndEdit control doesn't have focus).
As of now as far as I can tell everything is working close to this at the moment. All hot keys are deactivated when focus is in MessageWndEdit so the actual character is typed. Are you saying though you'd like it to have hotkeys enabled if its in the MessageWndEdit but not typing?

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: [PATCH] User Defined Hot Keys -- In Progress

#10 Post by Geoff the Medio »

fixIt wrote:I was referring to the boost states in HumanClientFSM, where I am currently deactivating all hot keys when the IntroMenu state is instantiated & activating all hot keys when the PlayingGame state is instantiated. Within HumanClientFSM I saw some sub states involving a Chatwnd/message but they all seemed to be only when a message is actually entered.
There are no FSM states for chat messages. There are FSM events related to *receiving* a chat network message (PlayerChat and LobbyChat) from the server, but those events don't have anything to do with the local player sending a chat message out, and don't cause FSM state transitions.
As of now I call Deactivate/Activate for the chat wnd within MessageWndEdit::gainfocus/losefocus, so I just wasn't sure if I was missing somewhere I could do this in HumanClientFSM.
Not in response to chat messages being sent, no.
I'm not familiar with the history scroll bar, how do you transfer focus from the MessageWndEdit to it?
I mean the player clicking on the history scroll bar, and thereby making it the focus control. (I'm not actually sure if the scrollbar can have focus, though...)
Are you saying though you'd like it to have hotkeys enabled if its in the MessageWndEdit but not typing?
No.

Post Reply