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:
Thomas Lübking 2011-08-22 13:53:03 +02:00
parent e022efe2a2
commit 117eb46684
6 changed files with 141 additions and 77 deletions

View file

@ -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)

View file

@ -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);
}

View file

@ -113,6 +113,8 @@ private:
KIntNumInput *delayFocus;
QCheckBox *separateScreenFocus;
QCheckBox *activeMouseScreen;
QWidget *focusNextToMouseContainer;
QCheckBox *focusNextToMouse;
KConfig *config;
bool standAlone;

View file

@ -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);

View file

@ -79,7 +79,7 @@ public:
*/
enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse };
FocusPolicy focusPolicy;
bool nextFocusPrefersMouse;
/**
Whether clicking on a window raises it in FocusFollowsMouse

View file

@ -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);