Merge pull request #2164 from ivan1986/add-keyboard-group
add keyboard layout switch selector
This commit is contained in:
commit
60df29d734
@ -30,6 +30,7 @@ This release contains contributions from (alphabetically by first name):
|
||||
in a Wayland session. (thanks Hector)
|
||||
- *keyboard* module now writes X11 layout configuration with variants
|
||||
for all non-ASCII (e.g. us) layouts. (thanks Ivan)
|
||||
- *keyboard* module now can configure keyboard switch. (thanks Ivan)
|
||||
|
||||
|
||||
# 3.3.0-alpha3 (2023-08-28)
|
||||
|
@ -160,6 +160,7 @@ Config::Config( QObject* parent )
|
||||
, m_keyboardModelsModel( new KeyboardModelsModel( this ) )
|
||||
, m_keyboardLayoutsModel( new KeyboardLayoutModel( this ) )
|
||||
, m_keyboardVariantsModel( new KeyboardVariantsModel( this ) )
|
||||
, m_KeyboardGroupSwitcherModel( new KeyboardGroupsSwitchersModel( this ) )
|
||||
{
|
||||
m_setxkbmapTimer.setSingleShot( true );
|
||||
|
||||
@ -190,25 +191,40 @@ Config::Config( QObject* parent )
|
||||
emit prettyStatusChanged();
|
||||
} );
|
||||
|
||||
connect( m_keyboardVariantsModel, &KeyboardVariantsModel::currentIndexChanged, this, &Config::xkbChanged );
|
||||
connect( m_keyboardVariantsModel,
|
||||
&KeyboardVariantsModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedVariant = m_keyboardVariantsModel->key( index );
|
||||
xkbChanged();
|
||||
emit prettyStatusChanged();
|
||||
} );
|
||||
connect( m_KeyboardGroupSwitcherModel,
|
||||
&KeyboardGroupsSwitchersModel::currentIndexChanged,
|
||||
[ & ]( int index )
|
||||
{
|
||||
m_selectedGroup = m_KeyboardGroupSwitcherModel->key( index );
|
||||
xkbChanged();
|
||||
emit prettyStatusChanged();
|
||||
} );
|
||||
|
||||
// If the user picks something explicitly -- not a consequence of
|
||||
// a guess -- then move to UserSelected state and stay there.
|
||||
connect( m_keyboardModelsModel, &KeyboardModelsModel::currentIndexChanged, this, &Config::selectionChange );
|
||||
connect( m_keyboardLayoutsModel, &KeyboardLayoutModel::currentIndexChanged, this, &Config::selectionChange );
|
||||
connect( m_keyboardVariantsModel, &KeyboardVariantsModel::currentIndexChanged, this, &Config::selectionChange );
|
||||
connect( m_KeyboardGroupSwitcherModel, &KeyboardGroupsSwitchersModel::currentIndexChanged, this, &Config::selectionChange );
|
||||
|
||||
m_selectedModel = m_keyboardModelsModel->key( m_keyboardModelsModel->currentIndex() );
|
||||
m_selectedLayout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).first;
|
||||
m_selectedVariant = m_keyboardVariantsModel->key( m_keyboardVariantsModel->currentIndex() );
|
||||
m_selectedGroup = m_KeyboardGroupSwitcherModel->key( m_KeyboardGroupSwitcherModel->currentIndex() );
|
||||
}
|
||||
|
||||
void
|
||||
Config::xkbChanged( int index )
|
||||
Config::xkbChanged()
|
||||
{
|
||||
// Set Xorg keyboard layout + variant
|
||||
m_selectedVariant = m_keyboardVariantsModel->key( index );
|
||||
|
||||
if ( m_setxkbmapTimer.isActive() )
|
||||
{
|
||||
m_setxkbmapTimer.stop();
|
||||
@ -271,8 +287,15 @@ Config::xkbApply()
|
||||
|
||||
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = xkbmap_query_grp_option();
|
||||
if ( !m_selectedGroup.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = "grp:" + m_selectedGroup;
|
||||
}
|
||||
|
||||
if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = xkbmap_query_grp_option();
|
||||
}
|
||||
if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() )
|
||||
{
|
||||
m_additionalLayoutInfo.groupSwitcher = "grp:alt_shift_toggle";
|
||||
@ -315,6 +338,12 @@ Config::keyboardVariants() const
|
||||
return m_keyboardVariantsModel;
|
||||
}
|
||||
|
||||
KeyboardGroupsSwitchersModel*
|
||||
Config::keyboardGroupsSwitchers() const
|
||||
{
|
||||
return m_KeyboardGroupSwitcherModel;
|
||||
}
|
||||
|
||||
static QPersistentModelIndex
|
||||
findLayout( const KeyboardLayoutModel* klm, const QString& currentLayout )
|
||||
{
|
||||
@ -647,7 +676,8 @@ Config::finalize()
|
||||
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
|
||||
{
|
||||
gs->insert( "keyboardAdditionalLayout", m_additionalLayoutInfo.additionalLayout );
|
||||
gs->insert( "keyboardAdditionalLayout", m_additionalLayoutInfo.additionalVariant );
|
||||
gs->insert( "keyboardAdditionalVariant", m_additionalLayoutInfo.additionalVariant );
|
||||
gs->insert( "keyboardGroupSwitcher", m_additionalLayoutInfo.groupSwitcher );
|
||||
gs->insert( "keyboardVConsoleKeymap", m_additionalLayoutInfo.vconsoleKeymap );
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class Config : public QObject
|
||||
Q_PROPERTY( KeyboardModelsModel* keyboardModelsModel READ keyboardModels CONSTANT FINAL )
|
||||
Q_PROPERTY( KeyboardLayoutModel* keyboardLayoutsModel READ keyboardLayouts CONSTANT FINAL )
|
||||
Q_PROPERTY( KeyboardVariantsModel* keyboardVariantsModel READ keyboardVariants CONSTANT FINAL )
|
||||
Q_PROPERTY( KeyboardGroupsSwitchersModel* keyboardGroupsSwitchersModel READ keyboardGroupsSwitchers CONSTANT FINAL )
|
||||
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
|
||||
|
||||
public:
|
||||
@ -58,6 +59,9 @@ public:
|
||||
* (dvorak).
|
||||
*/
|
||||
KeyboardVariantsModel* keyboardVariants() const;
|
||||
/* A group describes a toggle groups of change layouts
|
||||
*/
|
||||
KeyboardGroupsSwitchersModel* keyboardGroupsSwitchers() const;
|
||||
|
||||
/** @brief Call this to change application language
|
||||
*
|
||||
@ -87,7 +91,7 @@ private:
|
||||
* xkbChanged() is called when the selection changes, and triggers
|
||||
* a delayed call to xkbApply() which does the actual work.
|
||||
*/
|
||||
void xkbChanged( int index );
|
||||
void xkbChanged();
|
||||
void xkbApply();
|
||||
void locale1Apply();
|
||||
|
||||
@ -97,10 +101,12 @@ private:
|
||||
KeyboardModelsModel* m_keyboardModelsModel;
|
||||
KeyboardLayoutModel* m_keyboardLayoutsModel;
|
||||
KeyboardVariantsModel* m_keyboardVariantsModel;
|
||||
KeyboardGroupsSwitchersModel* m_KeyboardGroupSwitcherModel;
|
||||
|
||||
QString m_selectedLayout;
|
||||
QString m_selectedModel;
|
||||
QString m_selectedVariant;
|
||||
QString m_selectedGroup;
|
||||
|
||||
// Layout (and corresponding info) added if current one doesn't support ASCII (e.g. Russian or Japanese)
|
||||
AdditionalLayoutInfo m_additionalLayoutInfo;
|
||||
|
@ -823,3 +823,58 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/* This returns a reference to local, which is a terrible idea.
|
||||
* Good thing it's not meant to be compiled.
|
||||
*/
|
||||
class kb_groups : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
const QStringList& table()
|
||||
{
|
||||
return QStringList {
|
||||
tr("Alt+Caps Lock", "kb_group"),
|
||||
tr("Alt+Ctrl", "kb_group"),
|
||||
tr("Alt+Shift", "kb_group"),
|
||||
tr("Alt+Space", "kb_group"),
|
||||
tr("Any Win (while pressed)", "kb_group"),
|
||||
tr("Both Alts together", "kb_group"),
|
||||
tr("Both Alts together; AltGr alone chooses third level", "kb_group"),
|
||||
tr("Both Ctrls together", "kb_group"),
|
||||
tr("Both Shifts together", "kb_group"),
|
||||
tr("Caps Lock", "kb_group"),
|
||||
tr("Caps Lock (while pressed), Alt+Caps Lock for the original Caps Lock action", "kb_group"),
|
||||
tr("Caps Lock to first layout; Shift+Caps Lock to second layout", "kb_group"),
|
||||
tr("Ctrl+Left Win to first layout; Ctrl+Menu to second layout", "kb_group"),
|
||||
tr("Ctrl+Shift", "kb_group"),
|
||||
tr("Ctrl+Space", "kb_group"),
|
||||
tr("Left Alt", "kb_group"),
|
||||
tr("Left Alt (while pressed)", "kb_group"),
|
||||
tr("Left Alt+Left Shift", "kb_group"),
|
||||
tr("Left Ctrl", "kb_group"),
|
||||
tr("Left Ctrl to first layout; Right Ctrl to second layout", "kb_group"),
|
||||
tr("Left Ctrl+Left Shift", "kb_group"),
|
||||
tr("Left Ctrl+Left Win", "kb_group"),
|
||||
tr("Left Shift", "kb_group"),
|
||||
tr("Left Win", "kb_group"),
|
||||
tr("Left Win (while pressed)", "kb_group"),
|
||||
tr("Left Win to first layout; Right Win/Menu to second layout", "kb_group"),
|
||||
tr("Menu", "kb_group"),
|
||||
tr("Menu (while pressed), Shift+Menu for Menu", "kb_group"),
|
||||
tr("None", "kb_group"),
|
||||
tr("Right Alt", "kb_group"),
|
||||
tr("Right Alt (while pressed)", "kb_group"),
|
||||
tr("Right Alt+Right Shift", "kb_group"),
|
||||
tr("Right Ctrl", "kb_group"),
|
||||
tr("Right Ctrl (while pressed)", "kb_group"),
|
||||
tr("Right Ctrl+Right Shift", "kb_group"),
|
||||
tr("Right Shift", "kb_group"),
|
||||
tr("Right Win", "kb_group"),
|
||||
tr("Right Win (while pressed)", "kb_group"),
|
||||
tr("Scroll Lock", "kb_group"),
|
||||
tr("Shift+Caps Lock", "kb_group"),
|
||||
tr("Win+Space", "kb_group"),
|
||||
QString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,3 +272,23 @@ KeyboardVariantsModel::setVariants( QMap< QString, QString > variants )
|
||||
m_currentIndex = -1;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
KeyboardGroupsSwitchersModel::KeyboardGroupsSwitchersModel( QObject* parent )
|
||||
: XKBListModel( parent )
|
||||
{
|
||||
m_contextname = "kb_groups";
|
||||
|
||||
// The groups map is from human-readable names (!) to xkb identifier
|
||||
const auto groups = KeyboardGlobal::getKeyboardGroups();
|
||||
m_list.reserve( groups.count() );
|
||||
int index = 0;
|
||||
for ( const auto& key : groups.keys() )
|
||||
{
|
||||
// So here *key* is the key in the map, which is the human-readable thing,
|
||||
// while the struct fields are xkb-id, and human-readable
|
||||
m_list << ModelInfo { groups[ key ], key };
|
||||
index++;
|
||||
}
|
||||
|
||||
cDebug() << "Loaded" << m_list.count() << "keyboard groups";
|
||||
}
|
||||
|
@ -154,6 +154,20 @@ public:
|
||||
void setVariants( QMap< QString, QString > variants );
|
||||
};
|
||||
|
||||
/** @brief A list of groupsSwitcher (xkb id and human-readable)
|
||||
*
|
||||
* The list of group switching combinations `getKeyboardGroups()`
|
||||
* function can be used to update the switching when the two models
|
||||
* are related.
|
||||
*/
|
||||
class KeyboardGroupsSwitchersModel : public XKBListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KeyboardGroupsSwitchersModel( QObject* parent = nullptr );
|
||||
};
|
||||
|
||||
/** @brief Adjust to changes in application language.
|
||||
*/
|
||||
void retranslateKeyboardModels();
|
||||
|
@ -68,6 +68,12 @@ KeyboardPage::KeyboardPage( Config* config, QWidget* parent )
|
||||
ui->variantSelector->setCurrentIndex( model->index( model->currentIndex() ) );
|
||||
cDebug() << "Variants now total=" << model->rowCount() << "selected=" << model->currentIndex();
|
||||
}
|
||||
{
|
||||
auto* model = config->keyboardGroupsSwitchers();
|
||||
ui->groupSelector->setModel( model );
|
||||
ui->groupSelector->setCurrentIndex( model->currentIndex() );
|
||||
cDebug() << "Groups now total=" << model->rowCount() << "selected=" << model->currentIndex();
|
||||
}
|
||||
|
||||
connect( ui->buttonRestore,
|
||||
&QPushButton::clicked,
|
||||
@ -107,6 +113,16 @@ KeyboardPage::KeyboardPage( Config* config, QWidget* parent )
|
||||
ui->variantSelector->setCurrentIndex( m_config->keyboardVariants()->index( index ) );
|
||||
m_keyboardPreview->setVariant( m_config->keyboardVariants()->key( index ) );
|
||||
} );
|
||||
|
||||
connect( ui->groupSelector,
|
||||
QOverload< int >::of( &QComboBox::currentIndexChanged ),
|
||||
config->keyboardGroupsSwitchers(),
|
||||
QOverload< int >::of( &XKBListModel::setCurrentIndex ) );
|
||||
connect( config->keyboardGroupsSwitchers(),
|
||||
&KeyboardGroupsSwitchersModel::currentIndexChanged,
|
||||
ui->groupSelector,
|
||||
&QComboBox::setCurrentIndex );
|
||||
|
||||
CALAMARES_RETRANSLATE_SLOT( &KeyboardPage::retranslate );
|
||||
}
|
||||
|
||||
|
@ -118,23 +118,59 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="LE_TestKeyboard">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Type here to test your keyboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="LE_TestKeyboard">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Type here to test your keyboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Keyboard Switch:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="groupSelector">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -142,6 +178,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<tabstop>physicalModelSelector</tabstop>
|
||||
<tabstop>layoutSelector</tabstop>
|
||||
<tabstop>variantSelector</tabstop>
|
||||
<tabstop>groupSelector</tabstop>
|
||||
<tabstop>LE_TestKeyboard</tabstop>
|
||||
<tabstop>buttonRestore</tabstop>
|
||||
</tabstops>
|
||||
|
@ -199,6 +199,50 @@ parseKeyboardLayouts( const char* filepath )
|
||||
return layouts;
|
||||
}
|
||||
|
||||
static KeyboardGlobal::GroupsMap
|
||||
parseKeyboardGroupsSwitchers( const char* filepath )
|
||||
{
|
||||
KeyboardGlobal::GroupsMap models;
|
||||
|
||||
QFile fh( filepath );
|
||||
fh.open( QIODevice::ReadOnly );
|
||||
|
||||
if ( !fh.isOpen() )
|
||||
{
|
||||
cDebug() << "X11 Keyboard model definitions not found!";
|
||||
return models;
|
||||
}
|
||||
|
||||
QRegularExpression rx;
|
||||
rx.setPattern( "^\\s+grp:(\\S+)\\s+(\\w.*)\n$" );
|
||||
|
||||
bool optionSectionFound = findSection( fh, "! option" );
|
||||
// read the file until the end or until we break the loop
|
||||
while ( optionSectionFound && !fh.atEnd() )
|
||||
{
|
||||
QByteArray line = fh.readLine();
|
||||
|
||||
// check if we start a new section
|
||||
if ( line.startsWith( '!' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// here we are in the option section - find all "grp:" options
|
||||
|
||||
// insert into the model map
|
||||
QRegularExpressionMatch match = rx.match( line );
|
||||
if ( match.hasMatch() )
|
||||
{
|
||||
QString modelDesc = match.captured( 2 );
|
||||
QString model = match.captured( 1 );
|
||||
models.insert( modelDesc, model );
|
||||
}
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
|
||||
KeyboardGlobal::LayoutsMap
|
||||
KeyboardGlobal::getKeyboardLayouts()
|
||||
@ -212,3 +256,9 @@ KeyboardGlobal::getKeyboardModels()
|
||||
{
|
||||
return parseKeyboardModels( XKB_FILE );
|
||||
}
|
||||
|
||||
KeyboardGlobal::GroupsMap
|
||||
KeyboardGlobal::getKeyboardGroups()
|
||||
{
|
||||
return parseKeyboardGroupsSwitchers( XKB_FILE );
|
||||
}
|
||||
|
@ -30,9 +30,11 @@ public:
|
||||
|
||||
using LayoutsMap = QMap< QString, KeyboardInfo >;
|
||||
using ModelsMap = QMap< QString, QString >;
|
||||
using GroupsMap = QMap< QString, QString >;
|
||||
|
||||
static LayoutsMap getKeyboardLayouts();
|
||||
static ModelsMap getKeyboardModels();
|
||||
static GroupsMap getKeyboardGroups();
|
||||
};
|
||||
|
||||
#endif // KEYBOARDGLOBAL_H
|
||||
|
@ -15,14 +15,15 @@ Prints out a few tables of keyboard model, layout, variant names for
|
||||
use in translations.
|
||||
"""
|
||||
|
||||
def scrape_file(file, modelsset, layoutsset, variantsset):
|
||||
def scrape_file(file, modelsset, layoutsset, variantsset, groupsset):
|
||||
import re
|
||||
# These RE's match what is in keyboardglobal.cpp
|
||||
model_re = re.compile("^\\s+(\\S+)\\s+(\\w.*)\n$")
|
||||
layout_re = re.compile("^\\s+(\\S+)\\s+(\\w.*)\n$")
|
||||
variant_re = re.compile("^\\s+(\\S+)\\s+(\\S+): (\\w.*)\n$")
|
||||
group_re = re.compile("^\\s+grp:(\\S+)\\s+(\\w.*)\n$")
|
||||
|
||||
MODEL, LAYOUT, VARIANT = range(3)
|
||||
MODEL, LAYOUT, VARIANT, GROUP = range(4)
|
||||
state = None
|
||||
for line in file.readlines():
|
||||
# Handle changes in section
|
||||
@ -35,6 +36,9 @@ def scrape_file(file, modelsset, layoutsset, variantsset):
|
||||
elif line.startswith("! variant"):
|
||||
state = VARIANT
|
||||
continue
|
||||
elif line.startswith("! option"):
|
||||
state = GROUP
|
||||
continue
|
||||
elif not line.strip():
|
||||
state = None
|
||||
# Unchanged from last blank
|
||||
@ -53,6 +57,12 @@ def scrape_file(file, modelsset, layoutsset, variantsset):
|
||||
v = variant_re.match(line)
|
||||
name = v.groups()[2]
|
||||
variantsset.add(name)
|
||||
if state == GROUP:
|
||||
v = group_re.match(line)
|
||||
if v is None:
|
||||
continue
|
||||
name = v.groups()[1]
|
||||
groupsset.add(name)
|
||||
|
||||
|
||||
def write_set(file, label, set):
|
||||
@ -85,12 +95,15 @@ if __name__ == "__main__":
|
||||
models=set()
|
||||
layouts=set()
|
||||
variants=set()
|
||||
groups=set()
|
||||
variants.add( "Default" )
|
||||
groups.add( "None" )
|
||||
with open("/usr/local/share/X11/xkb/rules/base.lst", "r") as f:
|
||||
scrape_file(f, models, layouts, variants)
|
||||
scrape_file(f, models, layouts, variants, groups)
|
||||
with open("KeyboardData_p.cxxtr", "w") as f:
|
||||
f.write(cpp_header_comment)
|
||||
write_set(f, "kb_models", models)
|
||||
write_set(f, "kb_layouts", layouts)
|
||||
write_set(f, "kb_variants", variants)
|
||||
write_set(f, "kb_groups", groups)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user