Skip to content

Commit

Permalink
Store "keyboard focus" in settings. Fix focus edge cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
sudara committed Mar 19, 2024
1 parent ddd7e2a commit 65190b3
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 19 deletions.
2 changes: 1 addition & 1 deletion melatonin/component_model.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#include <utility>

#include "helpers/component_helpers.h"
#include "juce_gui_basics/juce_gui_basics.h"

namespace melatonin
Expand Down
11 changes: 8 additions & 3 deletions melatonin/helpers/overlay_mouse_listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,19 @@ namespace melatonin

void enable()
{
// replace me with an if condition if you are hitting this on purpose
jassert (!enabled);
enabled = true;
root->addMouseListener (this, true);
}

void disable()
{
enabled = false;
root->removeMouseListener (this);
if (enabled)
{
enabled = false;
root->removeMouseListener (this);
}
}

void mouseEnter (const juce::MouseEvent& event) override
Expand Down Expand Up @@ -95,7 +100,7 @@ namespace melatonin
}

// not sure if there's a better way to ask "is the mouse outside the plugin now?"
if (!root->contains(event.getEventRelativeTo(root).position))
if (!root->contains (event.getEventRelativeTo (root).position))
outlineComponentCallback (nullptr);
}

Expand Down
21 changes: 12 additions & 9 deletions melatonin/inspector_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

#include "components/inspector_image_button.h"
#include "helpers/misc.h"
#include "melatonin_inspector/melatonin/components/accesibility.h"
#include "melatonin_inspector/melatonin/components/box_model.h"
#include "melatonin_inspector/melatonin/components/color_picker.h"
#include "melatonin_inspector/melatonin/components/component_tree_view_item.h"
#include "melatonin_inspector/melatonin/components/preview.h"
#include "melatonin_inspector/melatonin/components/properties.h"
#include "melatonin_inspector/melatonin/components/accesibility.h"
#include "melatonin_inspector/melatonin/lookandfeel.h"

/*
Expand Down Expand Up @@ -86,8 +86,7 @@ namespace melatonin
searchBox.setColour (juce::TextEditor::focusedOutlineColourId, juce::Colours::transparentBlack);
searchBox.setTextToShowWhenEmpty ("Filter components...", colors::searchText);
searchBox.setJustification (juce::Justification::centredLeft);
searchBox.onEscapeKey = [&]
{
searchBox.onEscapeKey = [&] {
searchBox.setText ("");
searchBox.giveAwayKeyboardFocus();
lastSearchText = {};
Expand All @@ -99,7 +98,7 @@ namespace melatonin
auto searchText = searchBox.getText();
ensureTreeIsConstructed();

if ( lastSearchText.isNotEmpty() && ! searchText.startsWith ( lastSearchText ) )
if (lastSearchText.isNotEmpty() && !searchText.startsWith (lastSearchText))
{
getRoot()->validateSubItems();
}
Expand Down Expand Up @@ -136,8 +135,10 @@ namespace melatonin
toggleCallback (!inspectorEnabled);
};

// TODO: refactor this "on" state, it's terribly named
fpsToggle.on = false;
fpsToggle.onClick = [this] {
// TODO: I don't like that the "on" state implicitly was toggled here
settings->props->setValue ("fpsEnabled", fpsToggle.on);
toggleFPSCallback (fpsToggle.on);
};
Expand All @@ -147,9 +148,11 @@ namespace melatonin
searchBox.giveAwayKeyboardFocus();
};

tabToggle.on = false;
// TODO: sorta sketchy to "know" the enum default...
tabToggle.on = settings->props->getIntValue ("selectionMode", 0);
tabToggle.onClick = [this] {
toggleSelectionMode(tabToggle.on);
settings->props->setValue ("selectionMode", fpsToggle.on);
toggleSelectionMode (tabToggle.on);
};

// the tree view is empty even if inspector is enabled
Expand Down Expand Up @@ -263,7 +266,7 @@ namespace melatonin
colorPickerPanel.setBounds (colorPickerBounds.removeFromTop (32).removeFromLeft (200));

accessibilityPanel.setBounds (mainCol.removeFromTop (32));
accessibility.setBounds (mainCol.removeFromTop (accessibility.isVisible() ? 110 : 0).withTrimmedLeft(32));
accessibility.setBounds (mainCol.removeFromTop (accessibility.isVisible() ? 110 : 0).withTrimmedLeft (32));

propertiesPanel.setBounds (mainCol.removeFromTop (33)); // extra pixel for divider
properties.setBounds (mainCol.withTrimmedLeft (32));
Expand All @@ -281,7 +284,7 @@ namespace melatonin
tree.setBounds (treeViewBounds);
}

void displayComponentInfo (Component* component, bool collapseTreeBeforeSelection=false)
void displayComponentInfo (Component* component, bool collapseTreeBeforeSelection = false)
{
TRACE_COMPONENT();

Expand Down Expand Up @@ -315,7 +318,7 @@ namespace melatonin
}
}

void selectComponent (Component* component, bool collapseTreeBeforeSelection=false)
void selectComponent (Component* component, bool collapseTreeBeforeSelection = false)
{
TRACE_COMPONENT();

Expand Down
34 changes: 28 additions & 6 deletions melatonin_inspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ namespace melatonin
setupCallbacks();

setResizable (true, false); // calls resized

// order mattecrs, set the mode before we toggle on
setSelectionMode (static_cast<SelectionMode> (settings->props->getIntValue ("inspectorSelectionMode", FOLLOWS_MOUSE)));

toggle (inspectorEnabledAtStart);
}

Expand All @@ -95,6 +99,10 @@ namespace melatonin

this->removeKeyListener (&keyListener);

// the mouse listener is owned and removes itself on destruction
if (selectionMode == FOLLOWS_FOCUS)
juce::Desktop::getInstance().removeFocusChangeListener (this);

// needed, otherwise removing look and feel will save bounds
settings->props.reset();
setLookAndFeel (nullptr);
Expand Down Expand Up @@ -299,9 +307,12 @@ namespace melatonin

overlay.setVisible (newStatus);
if (newStatus)
// without this, target apps that have UI to open the inspector
// will select that piece of UI within the same click, see #70
juce::Timer::callAfterDelay (500, [&] { overlayMouseListener.enable(); });
{
if (selectionMode == FOLLOWS_MOUSE)
// without this, target apps that have UI to open the inspector
// will select that piece of UI within the same click, see #70
callAfterDelay (500, [&] { overlayMouseListener.enable(); });
}
else
{
clearSelections();
Expand Down Expand Up @@ -340,6 +351,7 @@ namespace melatonin
switch (selectionMode)
{
case FOLLOWS_FOCUS:
selectComponent (nullptr);
juce::Desktop::getInstance().removeFocusChangeListener (this);
break;
case FOLLOWS_MOUSE:
Expand All @@ -358,6 +370,9 @@ namespace melatonin
overlayMouseListener.enable();
break;
}

settings->props->setValue ("inspectorSelectionMode", selectionMode);
settings->saveIfNeeded();
}

private:
Expand Down Expand Up @@ -401,10 +416,17 @@ namespace melatonin

void globalFocusChanged (Component* focusedComponent) override
{
selectComponent (focusedComponent);
// nullptr focus events fire when focus is lost
if (focusedComponent == nullptr)
return;

// we only want to respond to focus events related to the UI under inspection (root)
// Unfortunately, we can't test to see if the focusedComponent is a child of root
// because JUCE UI like list boxes or text editors sometimes technically have no parent :/
if (focusedComponent != root && focusedComponent->getTopLevelComponent() != inspectorComponent.getTopLevelComponent())
selectComponent (focusedComponent);
}

private:
void timerCallback() override
{
for (auto& ms : juce::Desktop::getInstance().getMouseSources())
Expand All @@ -420,7 +442,7 @@ namespace melatonin
overlayMouseListener.selectComponentCallback = [this] (Component* c) { this->selectComponent (c, true); };
overlayMouseListener.componentStartDraggingCallback = [this] (Component* c, const juce::MouseEvent& e) { this->startDragComponent (c, e); };
overlayMouseListener.componentDraggedCallback = [this] (Component* c, const juce::MouseEvent& e) { this->dragComponent (c, e); };
overlayMouseListener.mouseExitCallback = [this]() { if (this->inspectorEnabled) inspectorComponent.redisplaySelectedComponent(); };
overlayMouseListener.mouseExitCallback = [this] { if (this->inspectorEnabled) inspectorComponent.redisplaySelectedComponent(); };

inspectorComponent.selectComponentCallback = [this] (Component* c) { this->selectComponent (c, false); };
inspectorComponent.outlineComponentCallback = [this] (Component* c) { this->outlineComponent (c); };
Expand Down

0 comments on commit 65190b3

Please sign in to comment.