add mouse preference against focus chain
there's currently no GUI config item, use kwriteconfig --file kwinrc --group Windows --key NextFocusPrefersMouse true qdbus org.kde.kwin /KWin reconfigure BUG: 159989 CCBUG: 80897 FIXED-IN: 4.8
This commit is contained in:
parent
e022efe2a2
commit
117eb46684
6 changed files with 141 additions and 77 deletions
|
@ -412,49 +412,77 @@ static inline bool isUsableFocusCandidate(Client *c, Client *prev, bool respectS
|
|||
bool Workspace::activateNextClient(Client* c)
|
||||
{
|
||||
// if 'c' is not the active or the to-become active one, do nothing
|
||||
if (!(c == active_client
|
||||
|| (should_get_focus.count() > 0 && c == should_get_focus.last())))
|
||||
if (!(c == active_client || (should_get_focus.count() > 0 && c == should_get_focus.last())))
|
||||
return false;
|
||||
|
||||
closeActivePopup();
|
||||
|
||||
if (c != NULL) {
|
||||
if (c == active_client)
|
||||
setActiveClient(NULL, Allowed);
|
||||
should_get_focus.removeAll(c);
|
||||
}
|
||||
if (focusChangeEnabled()) {
|
||||
if (options->focusPolicyIsReasonable()) {
|
||||
// search the focus_chain for a client to transfer focus to,
|
||||
Client* get_focus = NULL;
|
||||
|
||||
// first try to pass the focus to the (former) active clients leader
|
||||
if (c && (get_focus = c->transientFor()) &&
|
||||
isUsableFocusCandidate(get_focus, c, options->separateScreenFocus)) {
|
||||
raiseClient(get_focus); // also raise - we don't know where it came from
|
||||
} else { // nope, ask the focus chain for the next candidate
|
||||
get_focus = NULL; // reset
|
||||
for (int i = focus_chain[ currentDesktop()].size() - 1; i >= 0; --i) {
|
||||
Client* ci = focus_chain[ currentDesktop()].at(i);
|
||||
if (isUsableFocusCandidate(ci, c, options->separateScreenFocus)) {
|
||||
get_focus = ci;
|
||||
break; // we're done
|
||||
}
|
||||
}
|
||||
// if blocking focus, move focus to the desktop later if needed
|
||||
// in order to avoid flickering
|
||||
if (!focusChangeEnabled()) {
|
||||
focusToNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!options->focusPolicyIsReasonable())
|
||||
return false;
|
||||
|
||||
Client* get_focus = NULL;
|
||||
|
||||
if (options->nextFocusPrefersMouse) {
|
||||
QList<Client*>::const_iterator it = stackingOrder().constEnd();
|
||||
while (it != stackingOrder().constBegin()) {
|
||||
Client *client = *(--it);
|
||||
|
||||
// rule out clients which are not really visible.
|
||||
// the screen test is rather superflous for xrandr & twinview since the geometry would differ -> TODO: might be dropped
|
||||
if (!(client->isShown(false) && client->isOnCurrentDesktop() &&
|
||||
client->isOnCurrentActivity() && client->isOnScreen(c ? c->screen() : activeScreen())))
|
||||
continue;
|
||||
|
||||
if (client->geometry().contains(QCursor::pos())) {
|
||||
if (client != c && !client->isDesktop()) // should rather not happen, but it cannot get the focus. rest of usability is tested above
|
||||
get_focus = client;
|
||||
break; // unconditional break - we do not pass the focus to some client below an unusable one
|
||||
}
|
||||
|
||||
if (get_focus == NULL) // last chance: focus the desktop
|
||||
get_focus = findDesktop(true, currentDesktop());
|
||||
}
|
||||
}
|
||||
|
||||
if (get_focus != NULL)
|
||||
requestFocus(get_focus);
|
||||
else
|
||||
focusToNull();
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
// if blocking focus, move focus to the desktop later if needed
|
||||
// in order to avoid flickering
|
||||
if (!get_focus) { // no suitable window under the mouse -> find sth. else
|
||||
|
||||
// first try to pass the focus to the (former) active clients leader
|
||||
if (c && (get_focus = c->transientFor()) && isUsableFocusCandidate(get_focus, c, options->separateScreenFocus)) {
|
||||
raiseClient(get_focus); // also raise - we don't know where it came from
|
||||
} else {
|
||||
// nope, ask the focus chain for the next candidate
|
||||
get_focus = NULL; // reset from the inline assignment above
|
||||
for (int i = focus_chain[ currentDesktop()].size() - 1; i >= 0; --i) {
|
||||
Client* ci = focus_chain[ currentDesktop()].at(i);
|
||||
if (isUsableFocusCandidate(ci, c, options->separateScreenFocus)) {
|
||||
get_focus = ci;
|
||||
break; // we're done
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_focus == NULL) // last chance: focus the desktop
|
||||
get_focus = findDesktop(true, currentDesktop());
|
||||
|
||||
if (get_focus != NULL)
|
||||
requestFocus(get_focus);
|
||||
else
|
||||
focusToNull();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void Workspace::setCurrentScreen(int new_screen)
|
||||
|
|
|
@ -116,6 +116,44 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
|
||||
fcsBox->setLayout(gLay);
|
||||
|
||||
focusCombo = new KComboBox(fcsBox);
|
||||
focusCombo->setEditable(false);
|
||||
focusCombo->addItem(i18n("Click to Focus"), CLICK_TO_FOCUS);
|
||||
focusCombo->addItem(i18n("Focus Follows Mouse"), FOCUS_FOLLOWS_MOUSE);
|
||||
focusCombo->addItem(i18n("Focus Under Mouse"), FOCUS_UNDER_MOUSE);
|
||||
focusCombo->addItem(i18n("Focus Strictly Under Mouse"), FOCUS_STRICTLY_UNDER_MOUSE);
|
||||
focusCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
label = new QLabel(i18n("&Policy:"), this);
|
||||
label->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
|
||||
label->setBuddy(focusCombo);
|
||||
gLay->addWidget(label, 0, 0, 1, 2);
|
||||
gLay->addWidget(focusCombo, 0, 2);
|
||||
|
||||
|
||||
// FIXME, when more policies have been added to KWin
|
||||
wtstr = i18n("The focus policy is used to determine the active window, i.e."
|
||||
" the window you can work in. <ul>"
|
||||
" <li><em>Click to focus:</em> A window becomes active when you click into it."
|
||||
" This is the behavior you might know from other operating systems.</li>"
|
||||
" <li><em>Focus follows mouse:</em> Moving the mouse pointer actively on to a"
|
||||
" normal window activates it. New windows will receive the focus,"
|
||||
" without you having to point the mouse at them explicitly."
|
||||
" Very practical if you are using the mouse a lot.</li>"
|
||||
" <li><em>Focus under mouse:</em> The window that happens to be under the"
|
||||
" mouse pointer is active. If the mouse points nowhere, the last window"
|
||||
" that was under the mouse has focus."
|
||||
" New windows will not automatically receive the focus.</li>"
|
||||
" <li><em>Focus strictly under mouse:</em> Only the window under the mouse pointer is"
|
||||
" active. If the mouse points nowhere, nothing has focus.</li>"
|
||||
" </ul>"
|
||||
"Note that 'Focus under mouse' and 'Focus strictly under mouse' prevent certain"
|
||||
" features such as the Alt+Tab walk through windows dialog in the KDE mode"
|
||||
" from working properly."
|
||||
);
|
||||
focusCombo->setWhatsThis(wtstr);
|
||||
|
||||
connect(focusCombo, SIGNAL(activated(int)), this, SLOT(focusPolicyChanged()));
|
||||
|
||||
focusStealing = new KComboBox(this);
|
||||
focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "None"));
|
||||
focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "Low"));
|
||||
|
@ -147,46 +185,12 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
label = new QLabel(i18n("Focus stealing prevention level:"), this);
|
||||
label->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
|
||||
label->setBuddy(focusStealing);
|
||||
gLay->addWidget(label, 0, 0, 1, 2);
|
||||
gLay->addWidget(focusStealing, 0, 2);
|
||||
|
||||
focusCombo = new KComboBox(fcsBox);
|
||||
focusCombo->setEditable(false);
|
||||
focusCombo->addItem(i18n("Click to Focus"), CLICK_TO_FOCUS);
|
||||
focusCombo->addItem(i18n("Focus Follows Mouse"), FOCUS_FOLLOWS_MOUSE);
|
||||
focusCombo->addItem(i18n("Focus Under Mouse"), FOCUS_UNDER_MOUSE);
|
||||
focusCombo->addItem(i18n("Focus Strictly Under Mouse"), FOCUS_STRICTLY_UNDER_MOUSE);
|
||||
focusCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
label = new QLabel(i18n("&Policy:"), this);
|
||||
label->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
|
||||
label->setBuddy(focusCombo);
|
||||
gLay->addWidget(label, 1, 0, 1, 2);
|
||||
gLay->addWidget(focusCombo, 1, 2);
|
||||
gLay->addWidget(focusStealing, 1, 2);
|
||||
|
||||
|
||||
// FIXME, when more policies have been added to KWin
|
||||
wtstr = i18n("The focus policy is used to determine the active window, i.e."
|
||||
" the window you can work in. <ul>"
|
||||
" <li><em>Click to focus:</em> A window becomes active when you click into it."
|
||||
" This is the behavior you might know from other operating systems.</li>"
|
||||
" <li><em>Focus follows mouse:</em> Moving the mouse pointer actively on to a"
|
||||
" normal window activates it. New windows will receive the focus,"
|
||||
" without you having to point the mouse at them explicitly."
|
||||
" Very practical if you are using the mouse a lot.</li>"
|
||||
" <li><em>Focus under mouse:</em> The window that happens to be under the"
|
||||
" mouse pointer is active. If the mouse points nowhere, the last window"
|
||||
" that was under the mouse has focus."
|
||||
" New windows will not automatically receive the focus.</li>"
|
||||
" <li><em>Focus strictly under mouse:</em> Only the window under the mouse pointer is"
|
||||
" active. If the mouse points nowhere, nothing has focus.</li>"
|
||||
" </ul>"
|
||||
"Note that 'Focus under mouse' and 'Focus strictly under mouse' prevent certain"
|
||||
" features such as the Alt+Tab walk through windows dialog in the KDE mode"
|
||||
" from working properly."
|
||||
);
|
||||
focusCombo->setWhatsThis(wtstr);
|
||||
|
||||
connect(focusCombo, SIGNAL(activated(int)), this, SLOT(focusPolicyChanged()));
|
||||
focusNextToMouse = new QCheckBox(/*TODO 4.9 i__18n*/("When the active window disappears, pass focus to window under mouse"), this);
|
||||
gLay->addWidget(focusNextToMouse, 2, 2, 1, 1);
|
||||
focusNextToMouse->hide();
|
||||
|
||||
// autoraise delay
|
||||
autoRaiseOn = new QCheckBox(fcsBox);
|
||||
|
@ -199,9 +203,9 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
autoRaiseOnLabel = new QLabel(i18n("&Raise, with the following delay:"), this);
|
||||
autoRaiseOnLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
|
||||
autoRaiseOnLabel->setBuddy(autoRaise);
|
||||
gLay->addWidget(autoRaiseOn, 2, 0);
|
||||
gLay->addWidget(autoRaiseOnLabel, 2, 1);
|
||||
gLay->addWidget(autoRaise, 2, 2);
|
||||
gLay->addWidget(autoRaiseOn, 3, 0);
|
||||
gLay->addWidget(autoRaiseOnLabel, 3, 1);
|
||||
gLay->addWidget(autoRaise, 3, 2);
|
||||
|
||||
connect(focusCombo, SIGNAL(activated(int)), this, SLOT(setDelayFocusEnabled()));
|
||||
|
||||
|
@ -213,12 +217,13 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
delayFocusOnLabel = new QLabel(i18n("Delay focus by:"), this);
|
||||
delayFocusOnLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
|
||||
delayFocusOnLabel->setBuddy(delayFocus);
|
||||
gLay->addWidget(delayFocusOnLabel, 3, 1);
|
||||
gLay->addWidget(delayFocus, 3, 2);
|
||||
|
||||
gLay->addWidget(delayFocusOnLabel, 4, 1);
|
||||
gLay->addWidget(delayFocus, 4, 2);
|
||||
|
||||
clickRaiseOn = new QCheckBox(i18n("C&lick raises active window"), fcsBox);
|
||||
connect(clickRaiseOn, SIGNAL(toggled(bool)), this, SLOT(clickRaiseOnTog(bool)));
|
||||
gLay->addWidget(clickRaiseOn, 4, 0, 1, 3);
|
||||
gLay->addWidget(clickRaiseOn, 5, 0, 1, 3);
|
||||
|
||||
autoRaiseOn->setWhatsThis(i18n("When this option is enabled, a window in the background will automatically"
|
||||
" come to the front when the mouse pointer has been over it for some time."));
|
||||
|
@ -235,12 +240,12 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
" will automatically receive focus."));
|
||||
|
||||
separateScreenFocus = new QCheckBox(i18n("S&eparate screen focus"), fcsBox);
|
||||
gLay->addWidget(separateScreenFocus, 5, 0, 1, 3);
|
||||
gLay->addWidget(separateScreenFocus, 6, 0, 1, 3);
|
||||
wtstr = i18n("When this option is enabled, focus operations are limited only to the active Xinerama screen");
|
||||
separateScreenFocus->setWhatsThis(wtstr);
|
||||
|
||||
activeMouseScreen = new QCheckBox(i18n("Active screen follows &mouse"), fcsBox);
|
||||
gLay->addWidget(activeMouseScreen, 6, 0, 1, 3);
|
||||
gLay->addWidget(activeMouseScreen, 7, 0, 1, 3);
|
||||
wtstr = i18n("When this option is enabled, the active Xinerama screen (where new windows appear, for example)"
|
||||
" is the screen containing the mouse pointer. When disabled, the active Xinerama screen is the "
|
||||
" screen containing the focused window. By default this option is disabled for Click to focus and"
|
||||
|
@ -265,6 +270,7 @@ KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentD
|
|||
connect(delayFocus, SIGNAL(valueChanged(int)), SLOT(changed()));
|
||||
connect(separateScreenFocus, SIGNAL(clicked()), SLOT(changed()));
|
||||
connect(activeMouseScreen, SIGNAL(clicked()), SLOT(changed()));
|
||||
connect(focusNextToMouse, SIGNAL(clicked()), SLOT(changed()));
|
||||
|
||||
load();
|
||||
}
|
||||
|
@ -324,6 +330,8 @@ void KFocusConfig::focusPolicyChanged()
|
|||
|
||||
focusStealing->setDisabled(policyIndex == FOCUS_UNDER_MOUSE || policyIndex == FOCUS_STRICTLY_UNDER_MOUSE);
|
||||
|
||||
focusNextToMouse->setDisabled(policyIndex == FOCUS_UNDER_MOUSE || policyIndex == FOCUS_STRICTLY_UNDER_MOUSE);
|
||||
|
||||
}
|
||||
|
||||
void KFocusConfig::setDelayFocusEnabled()
|
||||
|
@ -418,6 +426,8 @@ void KFocusConfig::load(void)
|
|||
// TODO default to low for now
|
||||
setFocusStealing(cg.readEntry(KWIN_FOCUS_STEALING, 1));
|
||||
|
||||
focusNextToMouse->setChecked(cg.readEntry("NextFocusPrefersMouse", false));
|
||||
|
||||
emit KCModule::changed(false);
|
||||
}
|
||||
|
||||
|
@ -454,6 +464,10 @@ void KFocusConfig::save(void)
|
|||
|
||||
cg.writeEntry(KWIN_FOCUS_STEALING, focusStealing->currentIndex());
|
||||
|
||||
cg.writeEntry(KWIN_SEPARATE_SCREEN_FOCUS, separateScreenFocus->isChecked());
|
||||
cg.writeEntry(KWIN_ACTIVE_MOUSE_SCREEN, activeMouseScreen->isChecked());
|
||||
|
||||
cg.writeEntry("NextFocusPrefersMouse", focusNextToMouse->isChecked());
|
||||
|
||||
if (standAlone) {
|
||||
config->sync();
|
||||
|
@ -481,6 +495,7 @@ void KFocusConfig::defaults()
|
|||
// on by default for non click to focus policies
|
||||
setActiveMouseScreen(focusCombo->currentIndex() != 0);
|
||||
setDelayFocusEnabled();
|
||||
focusNextToMouse->setChecked(false);
|
||||
emit KCModule::changed(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,8 @@ private:
|
|||
KIntNumInput *delayFocus;
|
||||
QCheckBox *separateScreenFocus;
|
||||
QCheckBox *activeMouseScreen;
|
||||
QWidget *focusNextToMouseContainer;
|
||||
QCheckBox *focusNextToMouse;
|
||||
|
||||
KConfig *config;
|
||||
bool standAlone;
|
||||
|
|
|
@ -120,6 +120,8 @@ unsigned long Options::updateSettings()
|
|||
else if (val == "FocusStrictlyUnderMouse")
|
||||
focusPolicy = FocusStrictlyUnderMouse;
|
||||
|
||||
nextFocusPrefersMouse = config.readEntry("NextFocusPrefersMouse", false);
|
||||
|
||||
separateScreenFocus = config.readEntry("SeparateScreenFocus", false);
|
||||
activeMouseScreen = config.readEntry("ActiveMouseScreen", focusPolicy != ClickToFocus);
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
*/
|
||||
enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse };
|
||||
FocusPolicy focusPolicy;
|
||||
|
||||
bool nextFocusPrefersMouse;
|
||||
|
||||
/**
|
||||
Whether clicking on a window raises it in FocusFollowsMouse
|
||||
|
|
|
@ -1338,6 +1338,23 @@ bool Workspace::setCurrentDesktop(int new_desktop)
|
|||
focus_chain[currentDesktop()].contains(active_client) &&
|
||||
active_client->isShown(true) && active_client->isOnCurrentDesktop())
|
||||
c = active_client; // The requestFocus below will fail, as the client is already active
|
||||
// from actiavtion.cpp
|
||||
if (!c && options->nextFocusPrefersMouse) {
|
||||
QList<Client*>::const_iterator it = stackingOrder().constEnd();
|
||||
while (it != stackingOrder().constBegin()) {
|
||||
Client *client = *(--it);
|
||||
|
||||
if (!(client->isShown(false) && client->isOnDesktop(new_desktop) &&
|
||||
client->isOnCurrentActivity() && client->isOnScreen(activeScreen())))
|
||||
continue;
|
||||
|
||||
if (client->geometry().contains(QCursor::pos())) {
|
||||
if (!client->isDesktop())
|
||||
c = client;
|
||||
break; // unconditional break - we do not pass the focus to some client below an unusable one
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!c) {
|
||||
for (int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i) {
|
||||
Client* tmp = focus_chain[currentDesktop()].at(i);
|
||||
|
|
Loading…
Reference in a new issue