Git Product home page Git Product logo

melatonin_inspector's Introduction

Melatonin Component Inspector

A JUCE module that gives you the ability to inspect and visually edit (non-destructively) components in your UI.

It's inspired by Figma (where I prefer to design UI), web browser web inspectors and Jim Credland's Component Debugger juce-toys.

A big hearty thanks to Dmytro Kiro and Roland Rabien (aka FigBug) for contributing some great features!


✨✨
✨✨✨
...the features...
✨✨✨
✨✨

Browse and select components visually

Point n' click to inspect a component, see its size and distance to parent.

Explore the component hierarchy

Immediately gain clarity over parent/child relationships and see what components are currently visible.

Filter components by name. Names are derived from stock components, label/button text, or demangled class names.

Preview Component

See what exactly is drawing on a per-component basis, even when the component is hidden. A fixed transparency grid helps you understand which components and images have transparency.

Edit component position and spacing

There's like...4 different ways to do this, visually and numerically...

We also display component padding if you follow the convention of storing them as the component properties paddingLeft, paddingTop, paddingRight, paddingBottom. See my PaddedComponent base class as an example.

Inspect and modify component flags and properties

See the most important component properties at a glance, including look and feels, fonts for labels, etc. Where applicable, flags are editable!

Any custom properties you've added the component will also show up here and be editable.

AudioPluginHost - 2023-08-14 01

Nudge components around

Verify new values, get things pixel perfect.

View spacing relative to siblings/neighbors

Hold "alt" while component is selected. A Figma inspired feature.

Display and modify JUCE widget colors

No, it's not a christmas miracle, but we do magically display JUCE's friendly enum ColourIds names from the stock widgets.

See what that Slider's trackColourId is set to, and hey, go ahead and try out a new theme in real time.

(Just to be clear: The color changes are temporary, no code is edited!)

AudioPluginHost - 2023-08-14 38

Color picker

Accurately pinpoint colors. Click to pick and store one. Toggle between RGBA and HEX values.

FPS meter

Overlay an FPS meter on your Editor to get an intuitive understanding of your painting performance. Please see the FAQ for details on usage.

Display component performance in real time

A life saving feature.

See time spent exclusively in a component's paint method as well as conveniently provide you with a sum with all children.

Keep track of the max. Double click to repaint and get fresh timings. See setup paint timing.

AudioPluginHost - 2023-08-16 57

Installing with CMake

CMake option #1: FetchContent

Place this chunk o love somewhere before your call to juce_add_plugin or juce_add_gui_app:

Include (FetchContent)
FetchContent_Declare (melatonin_inspector
  GIT_REPOSITORY https://github.com/sudara/melatonin_inspector.git
  GIT_TAG origin/main
  SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/melatonin_inspector)
FetchContent_MakeAvailable (melatonin_inspector)

CMake option #2 git submodules

If you are a git submodule aficionado, life is great! Add this submodule to your project:

git submodule add -b main https://github.com/sudara/melatonin_inspector.git modules/melatonin_inspector

and then simply call add_subdirectory in your CMakeLists.txt. Remember, modules go before your main call to juce_add_plugin or juce_add_gui_app (this makes life easier in IDEs):

add_subdirectory (modules/melatonin_inspector)

To update melatonin_inspector down the road (gasp! maintained dependencies!?):

git submodule update --remote --merge modules/melatonin_inspector

CMake Step 2: Tell JUCE about the module

Wait wait, not so fast! You couldn't get away that easily.

After your juce_add_plugin call you will need to link your plugin to the module's target, for example:

target_link_libraries("YourProject" PRIVATE melatonin_inspector)

Note: you don't have to call juce_add_module. That's handled by our CMake.

If you use Projucer, add the module manually.

Installing with Projucer

If you're rolling old school, or just prefer Projucer life, you'll be happy to note that though JUCE doesn't make it easy we've bent over backwards to make sure our icons, etc are included in the module.

Download the module

You can still use git to add it as a submodule if you'd like stay up to date with any changes:

git submodule add -b main https://github.com/sudara/melatonin_inspector.git modules/melatonin_inspector

Or just download it and stick it somewhere.

Add it to the projucer

Just "Add a module from a specified folder" and you're done!

2. Add an include to your Plugin Editor

Include the module header:

#include "melatonin_inspector/melatonin_inspector.h"

3. Add the inspector as a private member to your Editor

The easiest way to get started is to pass a reference to the root component of your UI (typically the Editor itself like in this example, but you could also inspect anything that derives from juce::Component).

melatonin::Inspector inspector { *this };

If you prefer the inspector open in the disabled state by default, you can pass false as the second argument.

melatonin::Inspector inspector { *this, false };

4. Set it visible

When the inspector as an editor member, you can use cmd/ctrl i to toggle whether the inspector is enabled.

setVisible on the member will also pop the window open.

What I do is have a GUI toggle that pops open the window and enables inspection:

// open the inspector window
inspector.setVisible(true); 

// enable the inspector
inspector.toggle(true);

5. Optional: Make it smarter

Setting up as above means that the inspector will always be constructed with your editor. Clicking close on the inspector's DocumentWindow will just hide it while disabling inspection.

If you wrap the inspector with #if DEBUG this might be fine for you.

However, if you'd plan to ship a product that includes the inspector, or otherwise want to lazily construct it to be more efficient, use a unique_ptr instead and set the onClose callback to reset the pointer.

// PluginEditor.h
std::unique_ptr<melatonin::Inspector> inspector;

// in some button on-click logic
// replace `this` with a reference to your editor if necessary
if (!inspector)
{
    inspector = std::make_unique<melatonin::Inspector> (*this);
    inspector->onClose = [this]() { inspector.reset(); };
}

inspector->setVisible (true);

Thanks to @FigBug for this feature.

6. Optional: Setup component timing

Just #include modules/melatonin_inspector/melatonin/helpers/timing.h and then call the RAII helper at the top of a component's paint method:

void paint (juce::Graphics& g) override
{
    melatonin::ComponentTimer timer { this };

    // do all your expensive painting...

This simply times the method and stores it in the component's own properties. It will store up to 3 values named timing1, timing2, timing3.

Want automatic timings for every JUCE component, including stock widgets? Upvote this FR.

Want timings for your custom components right now? Do what I do and derive all your components from a juce::Component subclass which wraps the paint call and adds the helper before paint is called.

Check out the forum post for detail. Or, if you run a JUCE fork, you might prefer Roland's solution.

FAQ

Can I use this in a GUI app/standalone?

Yup! See the tests folder for an example.

Can I save my component resizes or edits?

Nope!

For that, one would need a component system relying on data for placement and size vs. code. See Daniel's Foley GUI Magic.

How is the component hierarchy created?

It traverses components from the root, building a TreeView.

In the special case of TabbedComponent, each tab is added as a child.

My FPS seems low, is it accurate?

It's a smoothed running average.

If you see low FPS rates, check the following:

  • Are you running just your plugin? Make sure other plugin UIs are closed.
  • I optimize the inspector for Debug usage, but it can be tough for complex UIs to hit 60fps in Debug, especially on macOS (see note below). See what happens in Release.
  • You might have legitimately expensive paint calls (esp. in Debug). You can verify this out via Perfetto.

On recent macOS, a repaint() on even small sections of a window (ie, what the FPS meter does) will cause the OS to paint the entire plugin window. You can use Flash Screen Updates in Quartz Debug to verify this. Because of this macOS behavior, the FPS meter will actually trigger full repaints of your UI, so anything expensive (especially in Debug) will slow down what the FPS meter reports.

If you are using the JUCE flag JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS, JUCE will internally manage the rectangles that need to be repainted, with the aim of being more precise/hygenic with what actually gets painted. This might be a good choice if your plugin already frequently repainting parts of the UI. But please don't switch over to that flag just to appease the FPS meter! It needs to be a choice you make depending on your internal testing (without the FPS meter in play).

Feel free to ask for other ideas in the forum thread.

I have a problem / feature request

Please do submit an Issue or PR on the repository! Or visit the official thread on the JUCE forum.

Contributing

Contributions are always welcome!

The inspector wouldn't be half as awesome without the help of the community.

If you'd like to contribute, look out for the issues tagged with "Good First Issue"

Note that CI tests for compilation and treats errors on both macOS and Windows as errors.

Assets

All assets are PNG exported at 2x.

Please see the CMakelists.txt file for details on how to add icons in a Projucer friendly way. There's a script for it!

melatonin_inspector's People

Contributors

andybrown91 avatar baconpaul avatar dikadk avatar drowaudio avatar figbug avatar imjimmi avatar ruurdadema avatar static-cats avatar sudara avatar tobiashienzsch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

melatonin_inspector's Issues

Ability to inspect the inspector

Working on UI with the inspector is great. But now I’m spoiled and on working UI without it (aka working on the inspector itself) is painful.

I want a dogfood icon that lets me open up another inspector to inspect the inspector.

Measuring Tape

I'd like to draw a rectangle over my app to measure what the width/height is of something

Search/Filter should show children

Right now children aren't shown because it doesn't match. This means if you want to drill down from the search, you'd have to cancel it out / makes workflow difficult

AudioPluginHost - 2023-01-19 49 2@2x

Make padding editable in the box model

Right now sudara's PaddedComponent stores the values in the component's properties, like so:

            int paddingTop = props["paddingTop"];
            int paddingBottom = props["paddingBottom"];
            int paddingLeft = props["paddingLeft"];
            int paddingRight = props["paddingRight"];

This lets us display padding without caring how it's made or handled by the implementation (in other words, PaddedComponent doesn't have to be distributed with the inspector).

To make this editable, probably just a tiny a bit of work needs to happen to observe these properties in PaddedComponent?

Search Field "filter" for Component names

I'd like a search box above the tree view which:

  • has placeholder "search" text
  • when text is typed, an underlined link appears to the right of the box "clear"
  • pressing enter is not required, typing text should live filter the component names
  • if a "leaf node" matches the search query, the entire parent hierarchy is kept
  • Otherwise the node is removed from the tree
  • The first result is highlighted (also selected in the overlay)
  • If there's no match, "No component found" text appears vertically and horizontally centered

Image

Holding alt when component is selected shows distance to parent or hovered element

This is a direct copy of behavior in Figma. Happy to provide an example file for exploring and matching behavior.

Basically, this changes the behavior of the overlay when alt is held

Spacing to parent

When

  1. a component is selected
  2. hovering over self or parent

then show spacing to parent.

  • Additional red outline around selected component
  • A red line connects the midpoint of each components side to the parent (so, in all 4 directions)
  • Red backgrounded labels midway on each line displaying distance in logical pixels

Screenshot 2021-04-20 Pluginz - Figma

Spacing to hovered element

When

  1. a component is selected
  2. hovering over another component

Show spacing to hovered element.

This requires:

  • Red bounding boxes on selected AND hovered component
  • Determining if vertical or horizontal line is best
  • Line from middle of source element edge (as usual)

Screenshot 2021-04-20 Pluginz - Figma

  • In the case that the elements only connect diagonally, draw dotted line at right angle to target

Figma - 2022-11-24 17@2x

Setup a "property editor" box

To start, this could show:

  • editable x/y
  • editable width/height
  • is opaque?
  • has a cached image?
  • alpha
  • focus state

Probably we want to put the tree view in its own column and put properties under the box model? Could play with it, maybe properties in their own col also ok.

I want to "collapse" (aka remove the extra column) when "enabled" is unchecked.

Image

Look at chrome "inspector" for initial guidance on the properties view (no "filter" or disclosure triangle needed, can just theme the JUCE property editor)
Google Chrome - 2022-11-24 11@2x

Trouble with FetchContent

Hi and thanks for this great piece of work! Very helpful :)

I went the FetchContent way when adding this to my project, but got an error at this line, because FetchContent will by default add a "-src" suffix to the name of the directory.

I could resolve this by adding a specific directory name like this:

include (FetchContent)

FetchContent_Declare (melatonin_inspector
  GIT_REPOSITORY https://github.com/sudara/melatonin_inspector.git
  GIT_TAG origin/main
  SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/melatonin_inspector)

FetchContent_MakeAvailable (melatonin_inspector)

target_link_libraries (yourTarget PRIVATE Melatonin::Inspector)

My apologies if this is somehow specific to my project!

Consider restoring original overlay hover behavior

Originally (before #30?) hovering over items in the overlay would:

  1. Open the tree view
  2. Display all dimensions and properties for the hovered component.

This was a nice way to explore the app, as you could very quickly cruise around looking for things without selecting everything.

juce::LookAndFeel destructor asserts on quit

Trying this in a standalone app, the assert in the juce::LookAndFeel class gets hit when I quit it.

This assertion is triggered if you try to delete a LookAndFeel object while something is still using it!

Fixed that by adding to the MelatoninInspector's destructor:

    setLookAndFeel (nullptr);
    overlay.setLookAndFeel (nullptr);
    panel.setLookAndFeel (nullptr);

Improve support for switching between precompiled + cmake assets

Because of wanting to maintain compatibility with Projuce (and the lack of a convention/mechanism for BinaryData in JUCE modules), updating icons is laborious and requires a number of manual steps, including uncommenting CMake code to run the BinaryData helper.

Ideally we have a CMake way to "recompile InspectorBinaryData assets and place them in the source."

Also, including the output .cpp files manually is sketchy, would be nice to find a projucer-friendly way of including an arbitrary number of cpp files.

This the current comment in the CMake code:

To work with CMake Assets:

  * Uncomment the following lines to generate the assets with cmake
  * Delete everything in "LatestCompliedAssets" folder so there's no conflict
  * Comment out the manual includes in melatonin_inspector.cpp

When ready to commit:

  * Comment out the CMake `InspectorBinaryData` target lines again
  * Move the new binary .cpp from your build folder to LatestCompiledAssets
  * Move the latest InspectorBinaryData.h to the main module folder
    (for me the files are in cmake-build-debug/modules/melatonin_inspector/juce_binarydata_MelatoninInspector
  * Add the .cpp includes manually to melatonin_inspector.cpp

Trouble with CMake configuration on MSVC

First off thanks for creating this tool! It's been quite useful for me so far, and I feel like I'm just scratching the surface.

I've had a slight issue with CMake configuration when building with MSVC. I was adding the JUCE module from CMake using add_subdirectory(melatonin_inspector), which causes the CMake configure command to fail when generating a Visual Studio project, with the following error:

CMake Error: Cannot open file for write: D:/a/ChowMultiTool/ChowMultiTool/build/modules/melatonin_inspector/Melatonin::Inspector.sln.tmp480a3
CMake Error: : System Error: Invalid argument
CMake Generate step failed.  Build files cannot be regenerated correctly.

The issue is that the CMake proejct name is Melatonin::Inspector, which I guess is an illegal file name for a MSVC .sln (likely because of the double colon).

Anyway, I can get around it pretty easily by doing a juce_add_module instead, but I figured I'd mention it just in case you hadn't seen this issue yet.

Access violation in optimized builds.

On Windows I'm seeing access violation in optimized builds. Works fine in Debug. Just wondering if you'd seen this? Having trouble figuring out the cause. Crash as the window opens.

>	[Inline Frame] Nexus.exe!juce::CharPointer_UTF8::getAndAdvance() Line 149	C++
 	Nexus.exe!juce::CharacterFunctions::compare<juce::CharPointer_UTF8,juce::CharPointer_UTF8>(juce::CharPointer_UTF8 s1, juce::CharPointer_UTF8 s2) Line 612	C++
 	[Inline Frame] Nexus.exe!juce::CharPointer_UTF8::compare(const juce::CharPointer_UTF8 other) Line 401	C++
 	[Inline Frame] Nexus.exe!juce::String::compare(const juce::String & other) Line 596	C++
 	Nexus.exe!juce::operator!=(const juce::String & s1, const juce::String & s2) Line 555	C++
 	[Inline Frame] Nexus.exe!juce::Button::setButtonText(const juce::String & newText) Line 98	C++
 	Nexus.exe!juce::BooleanPropertyComponent::refresh() Line 83	C++
 	Nexus.exe!juce::PropertyPanel::SectionComponent::SectionComponent(const juce::String & sectionTitle, const juce::Array<juce::PropertyComponent *,juce::DummyCriticalSection,0> & newProperties, bool sectionIsOpen, int extraPadding) Line 43	C++
 	Nexus.exe!juce::PropertyPanel::addProperties(const juce::Array<juce::PropertyComponent *,juce::DummyCriticalSection,0> & newProperties, int extraPaddingBetweenComponents) Line 259	C++
 	Nexus.exe!melatonin::Properties::updateProperties() Line 58	C++
 	[Inline Frame] Nexus.exe!melatonin::ComponentModel::removeListeners::<lambda_1>::operator()(melatonin::ComponentModel::Listener & listener) Line 123	C++
 	[Inline Frame] Nexus.exe!juce::ListenerList<melatonin::ComponentModel::Listener,juce::Array<melatonin::ComponentModel::Listener *,juce::DummyCriticalSection,0>>::call(melatonin::ComponentModel::removeListeners::<lambda_1> && callback) Line 140	C++
 	Nexus.exe!melatonin::ComponentModel::removeListeners() Line 122	C++
 	Nexus.exe!melatonin::ComponentModel::updateModel() Line 81	C++
 	Nexus.exe!melatonin::InspectorComponent::selectComponent(juce::Component * component, bool collapseTreeBeforeSelection) Line 245	C++
 	Nexus.exe!melatonin::InspectorComponent::toggle(bool enabled) Line 284	C++
 	[External Code]	
 	[Inline Frame] Nexus.exe!juce::InternalMessageQueue::dispatchMessage(juce::MessageManager::MessageBase * message) Line 192	C++
 	Nexus.exe!juce::InternalMessageQueue::dispatchMessages() Line 234	C++
 	Nexus.exe!juce::InternalMessageQueue::dispatchNextMessage(bool returnIfNoPendingMessages) Line 144	C++
 	[Inline Frame] Nexus.exe!juce::detail::dispatchNextMessageOnSystemQueue(bool returnIfNoPendingMessages) Line 263	C++
 	[Inline Frame] Nexus.exe!juce::MessageManager::runDispatchLoop() Line 112	C++
 	Nexus.exe!juce::JUCEApplicationBase::main() Line 265	C++
 	[External Code]	

Add FPS meter

It would be nice to be able to toggle an FPS meter in the header

Resizable should extend a few px outside the component's bounds

This might not be trivial to fix, but would improve UX.

So step 1 is to just investigate if a fix is possible, or a headache.

Right now the interactive area of the resizable box in the overlay ends at the component's bounds.

This is unideal and makes resizing fiddly.

This has a side effect of making the hover state of a parent component too easily triggered — the parent hover is not cleared until the mouse moves all the way back inside the component beyond the inner edge of the resizable.

Screenshot 2021-04-21 SineMachine - AudioPluginHost

Show if accessibility is being handled

This should basically report false for a lot of components. Not sure if it's possible to show which actions are being handled or what detail can happen here...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.