2
0
Fork 0
OB-Xd/Modules/gin/components/componentviewer.cpp

365 lines
9.9 KiB
C++
Executable File

/*==============================================================================
Copyright 2019 by Roland Rabien
For more information visit www.rabiensoftware.com
==============================================================================*/
Component* realGetComponent (Component& p, juce::Point<int> screenPos)
{
if (p.getScreenBounds().contains (screenPos))
{
for (auto c : p.getChildren())
if (auto r = realGetComponent (*c, screenPos))
return r;
return &p;
}
return nullptr;
}
Component* realGetComponentUnderMouse()
{
auto mouse = Desktop::getInstance().getMainMouseSource();
auto pos = mouse.getScreenPosition().toInt();
auto& desktop = Desktop::getInstance();
for (int i = desktop.getNumComponents(); --i >= 0;)
{
if (auto dtc = desktop.getComponent (i))
if (dtc->isVisible())
if (auto c = realGetComponent (*dtc, pos))
return c;
}
return {};
}
static String getClassName (Component* c)
{
#if __clang__ || __GNUC__
int status = 0;
if (char* demangled = abi::__cxa_demangle (typeid (*c).name(), nullptr, nullptr, &status))
{
auto res = String (demangled);
free (demangled);
return res;
}
return {};
#else
String res = typeid (*c).name();
if (res.startsWith ("class ")) res = res.substring (6);
if (res.startsWith ("struct ")) res = res.substring (7);
return res;
#endif
}
//==============================================================================
class ComponentViewer::Snapshot : public Component
{
public:
void update (Component* c, juce::Point<int> pos, int zoom_)
{
zoom = zoom_;
if (c != nullptr)
{
int w = getWidth() / zoom + 1;
int h = getHeight() / zoom + 1;
auto root = c->getTopLevelComponent();
auto rootPos = root->getLocalPoint (c, pos);
auto disp = Desktop::getInstance().getDisplays().findDisplayForPoint (c->localPointToGlobal (pos));
scale = float (disp.scale);
image = root->createComponentSnapshot ({rootPos.getX() - w / 2, rootPos.getY() - h / 2, w, h}, false, scale);
image = image.rescaled (w * zoom, h * zoom, Graphics::lowResamplingQuality);
repaint();
}
else
{
clear();
}
}
void clear()
{
image = {};
repaint();
}
Colour getColourUnderMouse()
{
if (! image.isNull() && zoom > 0)
{
int w = getWidth() / zoom + 1;
int h = getHeight() / zoom + 1;
int x = w / 2 * zoom;
int y = h / 2 * zoom;
return image.getPixelAt (x, y);
}
return {};
}
private:
void paint (Graphics& g) override
{
if (! image.isNull() && zoom > 0 && scale != 0.0f)
{
g.drawImageAt (image, 0, 0);
int w = getWidth() / zoom + 1;
int h = getHeight() / zoom + 1;
Rectangle<int> rc (w / 2 * zoom, h / 2 * zoom, int (zoom / scale), int (zoom / scale));
auto c = image.getPixelAt (rc.getX(), rc.getY());
g.setColour (c.contrasting());
g.drawRect (rc, 1);
}
}
Image image;
int zoom = 10;
float scale = 1.0f;
};
//==============================================================================
class ComponentViewer::ContentComponent : public Component,
private Timer,
private Slider::Listener
{
public:
ContentComponent (PropertiesFile& settings_)
: settings (settings_)
{
Font f (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain);
addAndMakeVisible (mouseDetails);
mouseDetails.setMultiLine (true, false);
mouseDetails.setFont (f);
mouseDetails.setReadOnly (true);
addAndMakeVisible (componentDetails);
componentDetails.setFont (f);
componentDetails.setMultiLine (true, false);
componentDetails.setReadOnly (true);
addAndMakeVisible (snapshotDetails);
snapshotDetails.setFont (f);
snapshotDetails.setReadOnly (true);
snapshotDetails.setJustification (Justification::centred);
addAndMakeVisible (zoom);
zoom.setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
zoom.setRange (1, 30, 1);
zoom.setValue (settings.getIntValue ("ginZoom", 10));
zoom.addListener (this);
addAndMakeVisible (snapshot);
Desktop::getInstance().addGlobalMouseListener (this);
}
~ContentComponent() override
{
Desktop::getInstance().removeGlobalMouseListener (this);
}
void resized() override
{
auto rc = getLocalBounds();
mouseDetails.setBounds (rc.removeFromTop (50));
componentDetails.setBounds (rc.removeFromTop (int (rc.getHeight() * 0.4)));
auto row = rc.removeFromTop (25);
int w = row.getWidth() / 3;
zoom.setBounds (row.removeFromLeft (w));
snapshotDetails.setBounds (row.removeFromLeft (w));
snapshot.setBounds (rc);
}
void mouseUp (const MouseEvent& ) override { updateComponentDetailsAsync(); }
void mouseDown (const MouseEvent& ) override { updateComponentDetailsAsync(); }
void mouseDrag (const MouseEvent& ) override { updateComponentDetailsAsync(); }
void mouseMove (const MouseEvent& ) override { updateComponentDetailsAsync(); }
void updateComponentDetailsAsync()
{
if (! isTimerRunning())
startTimer (50);
}
void timerCallback() override
{
stopTimer();
updateComponentDetails();
}
void sliderValueChanged (Slider*) override
{
settings.setValue ("ginZoom", int (zoom.getValue()));
updateComponentDetails();
}
void updateComponentDetails()
{
auto mouse = Desktop::getInstance().getMainMouseSource();
auto pos = mouse.getScreenPosition().toInt();
StringArray componentHierarchy;
String cursorPos, colourDetails;
if (auto c = realGetComponentUnderMouse())
{
snapshot.update (c, c->getLocalPoint (nullptr, pos), int (zoom.getValue()));
componentHierarchy = getComponentHierarchy (c);
cursorPos += "Component: (" + c->getLocalPoint (nullptr, pos).toString() + ")\n";
cursorPos += "Window: (" + c->getTopLevelComponent()->getLocalPoint (nullptr, pos).toString() + ")\n";
auto col = snapshot.getColourUnderMouse();
colourDetails = col.toDisplayString (true);
}
else
{
snapshot.clear();
}
cursorPos += "Screen: (" + pos.toString() + ")";
mouseDetails.setText (cursorPos);
snapshotDetails.setText (colourDetails);
componentDetails.setText (componentHierarchy.joinIntoString ("\n"));
}
StringArray getComponentHierarchy (Component* c)
{
StringArray res;
while (c != nullptr)
{
String str;
str += ("[" + String (getClassName (c)) + "]").paddedRight (' ', 60);
str += (" \"" + c->getName() + "\"").paddedRight (' ', 20);
str += (" (" + c->getBounds().toString() + ")").paddedRight (' ', 20);
str += String (c->isOpaque() ? " Opaque" : "").paddedRight (' ', 8);
str += String (c->isPaintingUnclipped() ? " Unclipped" : "").paddedRight (' ', 11);
res.add (str);
c = c->getParentComponent();
}
return res;
}
PropertiesFile& settings;
TextEditor mouseDetails, componentDetails, snapshotDetails;
Slider zoom;
Snapshot snapshot;
};
//==============================================================================
ComponentViewer::ComponentViewer (Component* toTrack_, PropertiesFile* settings_, bool alwaysOnTop)
: DocumentWindow ("Component Viewer Window", Colours::white, allButtons, true),
toTrack (toTrack_)
{
if (settings_ != nullptr)
{
settings.set (settings_, false);
}
else
{
PropertiesFile::Options opts;
opts.applicationName = "Gin";
opts.filenameSuffix = ".xml";
opts.folderName = "Gin";
opts.osxLibrarySubFolder = "Application Support";
opts.commonToAllUsers = false;
opts.ignoreCaseOfKeyNames = false;
opts.doNotSave = false;
opts.millisecondsBeforeSaving = 1;
opts.storageFormat = juce::PropertiesFile::storeAsXML;
settings.set (new PropertiesFile (opts), true);
}
if (toTrack != nullptr)
toTrack->addComponentListener (this);
auto position = settings->getValue ("ginComponentViewerPosition", "");
if (position.isNotEmpty())
restoreWindowStateFromString (position);
else
centreWithSize (640, 480);
setVisible (true);
setAlwaysOnTop (alwaysOnTop);
setResizable (true, false);
setContentOwned (new ContentComponent (*settings), false);
onClose = [this] { delete this; };
}
ComponentViewer::~ComponentViewer()
{
if (toTrack != nullptr)
toTrack->removeComponentListener (this);
saveWindowPosition();
}
void ComponentViewer::closeButtonPressed()
{
if (onClose)
onClose();
}
void ComponentViewer::moved()
{
DocumentWindow::resized();
saveWindowPosition();
}
void ComponentViewer::resized()
{
DocumentWindow::resized();
saveWindowPosition();
}
void ComponentViewer::saveWindowPosition()
{
if (settings != nullptr)
{
settings->setValue ("ginComponentViewerPosition", getWindowStateAsString());
settings->saveIfNeeded();
}
}
void ComponentViewer::componentBeingDeleted (Component&)
{
settings.reset();
if (toTrack != nullptr)
toTrack = nullptr;
delete this;
}
void ComponentViewer::lookAndFeelChanged()
{
auto& lf = getLookAndFeel ();
setBackgroundColour (lf.findColour (ResizableWindow::backgroundColourId));
}