diff --git a/activation.cpp b/activation.cpp index b50a3253f4..37f0070414 100644 --- a/activation.cpp +++ b/activation.cpp @@ -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::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) diff --git a/kcmkwin/kwinoptions/windows.cpp b/kcmkwin/kwinoptions/windows.cpp index ca25f25b7b..30c94c0ae1 100644 --- a/kcmkwin/kwinoptions/windows.cpp +++ b/kcmkwin/kwinoptions/windows.cpp @@ -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. " + "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. " - "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); } diff --git a/kcmkwin/kwinoptions/windows.h b/kcmkwin/kwinoptions/windows.h index ca0465240f..3fadf2c94f 100644 --- a/kcmkwin/kwinoptions/windows.h +++ b/kcmkwin/kwinoptions/windows.h @@ -113,6 +113,8 @@ private: KIntNumInput *delayFocus; QCheckBox *separateScreenFocus; QCheckBox *activeMouseScreen; + QWidget *focusNextToMouseContainer; + QCheckBox *focusNextToMouse; KConfig *config; bool standAlone; diff --git a/options.cpp b/options.cpp index a66a60855d..d496569468 100644 --- a/options.cpp +++ b/options.cpp @@ -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); diff --git a/options.h b/options.h index 90c204b82c..9dc29cf679 100644 --- a/options.h +++ b/options.h @@ -79,7 +79,7 @@ public: */ enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse }; FocusPolicy focusPolicy; - + bool nextFocusPrefersMouse; /** Whether clicking on a window raises it in FocusFollowsMouse diff --git a/workspace.cpp b/workspace.cpp index 6a8e2df2ea..3c67ae6085 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -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::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);