From fd125d3c1b0993e4b8849aa069a99477cc972b95 Mon Sep 17 00:00:00 2001 From: Timothy Schoen Date: Mon, 9 Dec 2024 01:37:55 +0100 Subject: [PATCH] WIP: inspector design improvements --- Source/Components/PropertiesPanel.h | 180 ++++++++++++++++++++++++++-- Source/LookAndFeel.cpp | 4 +- Source/Objects/AtomHelper.h | 4 +- Source/Objects/IEMHelper.h | 22 ++-- Source/Objects/NumberObject.h | 5 +- Source/Objects/ObjectParameters.h | 6 +- Source/Sidebar/Inspector.h | 13 +- 7 files changed, 198 insertions(+), 36 deletions(-) diff --git a/Source/Components/PropertiesPanel.h b/Source/Components/PropertiesPanel.h index 1b297ac0b..f5d11985f 100644 --- a/Source/Components/PropertiesPanel.h +++ b/Source/Components/PropertiesPanel.h @@ -111,7 +111,7 @@ class PropertiesPanel : public Component { auto titleX = x; if (parent.titleAlignment == AlignWithPropertyName) { - titleX += 8; + titleX += 12; } auto title = getName(); @@ -123,17 +123,15 @@ class PropertiesPanel : public Component { auto propertyBounds = Rectangle(x, titleHeight + 8.0f, width, getHeight() - (titleHeight + 16.0f)); - // Don't draw the shadow if the background colour has opacity if (parent.drawShadowAndOutline) { Path p; p.addRoundedRectangle(propertyBounds.reduced(3.0f), Corners::largeCornerRadius); dropShadow->render(g, p); } - g.setColour(findColour(parent.panelColour)); + g.setColour(findColour(parent.panelColour).withAlpha(parent.panelAlpha)); g.fillRoundedRectangle(propertyBounds, Corners::largeCornerRadius); - // Don't draw the outline if the background colour has opacity if (parent.drawShadowAndOutline) { g.setColour(findColour(parent.separatorColour)); g.drawRoundedRectangle(propertyBounds, Corners::largeCornerRadius, 1.0f); @@ -164,7 +162,7 @@ class PropertiesPanel : public Component { for (int i = 0; i < propertyComponents.size() - 1; i++) { auto y = propertyComponents[i]->getBottom() + padding; - g.drawHorizontalLine(y, x, x + width); + g.drawHorizontalLine(y, x + 10, (x + width) - 10); } } @@ -482,7 +480,8 @@ class PropertiesPanel : public Component { }; struct BoolComponent : public PropertiesPanelProperty - , public Value::Listener { + , public Value::Listener + { BoolComponent(String const& propertyName, Value& value, StringArray options) : PropertiesPanelProperty(propertyName) , textOptions(std::move(options)) @@ -593,6 +592,167 @@ class PropertiesPanel : public Component { StringArray textOptions; Value toggleStateValue; }; + + struct InspectorBoolComponent : public BoolComponent + { + using BoolComponent::BoolComponent; + + void paint(Graphics& g) override + { + bool isDown = getValue(toggleStateValue); + + auto bounds = getLocalBounds().toFloat().removeFromRight(getWidth() / (2.0f - hideLabel)); + auto buttonBounds = bounds.reduced(6); + + auto contrast = isDown ? 0.2f : 0.05f; + if(isMouseOver()) contrast += 0.025; + // Add some alpha to make it look good on any background... + g.setColour(findColour(PlugDataColour::sidebarActiveBackgroundColourId).contrasting(contrast).withAlpha(0.3f)); + g.fillRoundedRectangle(buttonBounds, Corners::defaultCornerRadius); + + auto textColour = findColour(PlugDataColour::panelTextColourId); + + if (!isEnabled()) { + textColour = findColour(PlugDataColour::panelTextColourId).withAlpha(0.5f); + } + Fonts::drawText(g, textOptions[isDown], bounds, textColour, 14.0f, Justification::centred); + + // Paint label + PropertiesPanelProperty::paint(g); + } + + PropertiesPanelProperty* createCopy() override + { + return new InspectorBoolComponent(getName(), toggleStateValue, textOptions); + } + + }; + + struct InspectorColourComponent : public PropertiesPanelProperty + , public Value::Listener { + + struct SwatchComponent : public Component { + + explicit SwatchComponent(Value const& colour) + { + colourValue.referTo(colour); + } + + void paint(Graphics& g) override + { + auto colour = Colour::fromString(colourValue.toString()); + + g.setColour(isMouseOver() ? colour.brighter(0.4f) : colour); + g.fillRoundedRectangle(getLocalBounds().toFloat(), Corners::defaultCornerRadius); + g.setColour(colour.darker(0.2f)); + g.drawRoundedRectangle(getLocalBounds().toFloat(), Corners::defaultCornerRadius, 0.8f); + } + + void mouseEnter(MouseEvent const& e) override + { + repaint(); + } + + void mouseExit(MouseEvent const& e) override + { + repaint(); + } + + Value colourValue; + }; + + InspectorColourComponent(String const& propertyName, Value& value) + : PropertiesPanelProperty(propertyName) + , swatchComponent(value) + { + + currentColour.referTo(value); + setWantsKeyboardFocus(true); + + currentColour.addListener(this); + + swatchComponent.setInterceptsMouseClicks(false, false); + addAndMakeVisible(swatchComponent); + + addAndMakeVisible(hexValueEditor); + hexValueEditor.setJustificationType(Justification::centred); + hexValueEditor.setInterceptsMouseClicks(false, true); + + hexValueEditor.onEditorShow = [this](){ + hexValueEditor.getCurrentTextEditor()->setInputRestrictions(7, "#0123456789ABCDEFabcdef"); + }; + + hexValueEditor.onEditorHide = [this]() { + colour = String("ff") + hexValueEditor.getText().substring(1).toLowerCase(); + currentColour.setValue(colour); + }; + + hexValueEditor.onTextChange = [this]() { + colour = String("ff") + hexValueEditor.getText().substring(1).toLowerCase(); + }; + + updateHexValue(); + + setLookAndFeel(&LookAndFeel::getDefaultLookAndFeel()); + + repaint(); + } + + ~InspectorColourComponent() override + { + currentColour.removeListener(this); + } + + PropertiesPanelProperty* createCopy() override + { + return new InspectorColourComponent(getName(), currentColour); + } + + void updateHexValue() + { + hexValueEditor.setColour(Label::textColourId, Colour::fromString(currentColour.toString()).contrasting(0.95f)); + hexValueEditor.setText(String("#") + currentColour.toString().substring(2).toUpperCase(), dontSendNotification); + } + + void resized() override + { + auto bounds = getLocalBounds().removeFromRight(getWidth() / (2 - hideLabel)).reduced(6); + swatchComponent.setBounds(bounds); + hexValueEditor.setBounds(bounds); + } + + void valueChanged(Value& v) override + { + if (v.refersToSameSourceAs(currentColour)) { + updateHexValue(); + repaint(); + } + } + + void mouseUp(MouseEvent const& e) override + { + if(e.getNumberOfClicks() == 2) + { + hexValueEditor.showEditor(); + } + else { + auto pickerBounds = getScreenBounds().expanded(5); + ColourPicker::getInstance().show(getTopLevelComponent(), false, Colour::fromString(currentColour.toString()), pickerBounds, [_this = SafePointer(this)](Colour c) { + if (!_this) + return; + + _this->currentColour = c.toString(); + _this->repaint(); + }); + } + } + + private: + SwatchComponent swatchComponent; + Value currentColour; + Value colour; + Label hexValueEditor; + }; struct ColourComponent : public PropertiesPanelProperty , public Value::Listener { @@ -1137,6 +1297,11 @@ class PropertiesPanel : public Component { { panelColour = newPanelColourId; } + + void setPanelAlpha(float panelTransparency) + { + panelAlpha = panelTransparency; + } void setSeparatorColour(int newSeparatorColourId) { @@ -1185,8 +1350,9 @@ class PropertiesPanel : public Component { TitleAlignment titleAlignment = AlignWithSection; int panelColour; int separatorColour; + float panelAlpha = 1.0f; bool drawShadowAndOutline = true; - int titleHeight = 26; + int titleHeight = 28; int contentWidth = 600; BouncingViewport viewport; PropertyHolderComponent* propertyHolderComponent; diff --git a/Source/LookAndFeel.cpp b/Source/LookAndFeel.cpp index dfc7f5a4f..6d1b9bed1 100644 --- a/Source/LookAndFeel.cpp +++ b/Source/LookAndFeel.cpp @@ -833,12 +833,12 @@ void PlugDataLook::drawPropertyComponentLabel(Graphics& g, int width, int height auto indent = jmin(10, component.getWidth() / 10); auto colour = component.findColour(PropertyComponent::labelTextColourId) - .withMultipliedAlpha(component.isEnabled() ? 1.0f : 0.6f); + .withMultipliedAlpha(component.isEnabled() ? 0.72f : 0.3f); auto textW = jmin(300, component.getWidth() / 2); auto r = Rectangle(textW, 0, component.getWidth() - textW, component.getHeight() - 1); - Fonts::drawFittedText(g, component.getName(), indent, r.getY(), r.getX(), r.getHeight(), colour, 1, 1.0f, (float)jmin(height, 24) * 0.65f, Justification::centredLeft); + Fonts::drawFittedText(g, component.getName(), indent + 2, r.getY(), r.getX(), r.getHeight(), colour, 1, 1.0f, (float)jmin(height, 24) * 0.65f, Justification::centredLeft); } void PlugDataLook::drawPropertyPanelSectionHeader(Graphics& g, String const& name, bool isOpen, int width, int height) diff --git a/Source/Objects/AtomHelper.h b/Source/Objects/AtomHelper.h index a8160cec3..9088a52ab 100644 --- a/Source/Objects/AtomHelper.h +++ b/Source/Objects/AtomHelper.h @@ -53,8 +53,8 @@ class AtomHelper { objectParameters.addParamCombo("Font height", cDimensions, &fontSize, { "auto", "8", "10", "12", "16", "24", "36" }); objectParameters.addParamReceiveSymbol(&receiveSymbol); objectParameters.addParamSendSymbol(&sendSymbol); - objectParameters.addParamString("Label", cLabel, &labelText, ""); - objectParameters.addParamCombo("Label Position", cLabel, &labelPosition, { "left", "right", "top", "bottom" }); + objectParameters.addParamString("Text", cLabel, &labelText, ""); + objectParameters.addParamCombo("Position", cLabel, &labelPosition, { "left", "right", "top", "bottom" }); } void update() diff --git a/Source/Objects/IEMHelper.h b/Source/Objects/IEMHelper.h index d73524a4f..ca4bb4cbe 100644 --- a/Source/Objects/IEMHelper.h +++ b/Source/Objects/IEMHelper.h @@ -54,8 +54,7 @@ class IEMHelper { gui->getLookAndFeel().setColour(Slider::backgroundColourId, sliderBackground); if (auto iemgui = ptr.get()) { - labelX = iemgui->x_ldx; - labelY = iemgui->x_ldy; + labelPosition = VarArray { var(iemgui->x_ldx), var(iemgui->x_ldy) }; } labelHeight = getFontHeight(); @@ -85,11 +84,10 @@ class IEMHelper { params.addParamReceiveSymbol(&receiveSymbol); params.addParamSendSymbol(&sendSymbol); } - params.addParamString("Label", cLabel, &labelText, ""); + params.addParamString("Text", cLabel, &labelText, ""); params.addParamColourLabel(&labelColour); - params.addParamInt("Label X", cLabel, &labelX, labelPosX); - params.addParamInt("Label Y", cLabel, &labelY, labelPosY); - params.addParamInt("Label Height", cLabel, &labelHeight, labelHeightY); + params.addParamRange("Position", cLabel, &labelPosition, {labelPosX, labelPosY}); + params.addParamInt("Height", cLabel, &labelHeight, labelHeightY); params.addParamBool("Initialise", cGeneral, &initialise, { "No", "Yes" }, 0); return params; @@ -176,8 +174,7 @@ class IEMHelper { } case hash("label_pos"): { if (atoms.size() >= 2) { - gui->setParameterExcludingListener(labelX, static_cast(atoms[0].getFloat())); - gui->setParameterExcludingListener(labelY, static_cast(atoms[1].getFloat())); + gui->setParameterExcludingListener(labelPosition, VarArray { var(atoms[0].getFloat()), var(atoms[1].getFloat()) }); gui->updateLabel(); } return true; @@ -246,8 +243,8 @@ class IEMHelper { } else if (v.refersToSameSourceAs(labelColour)) { setLabelColour(Colour::fromString(labelColour.toString())); gui->updateLabel(); - } else if (v.refersToSameSourceAs(labelX) || v.refersToSameSourceAs(labelY)) { - setLabelPosition({ getValue(labelX), getValue(labelY) }); + } else if (v.refersToSameSourceAs(labelPosition)) { + setLabelPosition({ labelPosition.getValue().getArray()->getReference(0), labelPosition.getValue().getArray()->getReference(1) }); gui->updateLabel(); } else if (v.refersToSameSourceAs(labelHeight)) { gui->limitValueMin(labelHeight, 4.f); @@ -538,9 +535,8 @@ class IEMHelper { Value secondaryColour = SynchronousValue(); Value labelColour = SynchronousValue(); - Value labelX = SynchronousValue(0.0f); - Value labelY = SynchronousValue(0.0f); - Value labelHeight = SynchronousValue(18.0f); + Value labelPosition; + Value labelHeight = SynchronousValue(); Value labelText = SynchronousValue(); Value initialise = SynchronousValue(); diff --git a/Source/Objects/NumberObject.h b/Source/Objects/NumberObject.h index c00c37cce..38b04c34e 100644 --- a/Source/Objects/NumberObject.h +++ b/Source/Objects/NumberObject.h @@ -99,10 +99,9 @@ class NumberObject final : public ObjectBase { objectParameters.addParamColourBG(&iemHelper.secondaryColour); objectParameters.addParamReceiveSymbol(&iemHelper.receiveSymbol); objectParameters.addParamSendSymbol(&iemHelper.sendSymbol); - objectParameters.addParamString("Label", cLabel, &iemHelper.labelText, ""); + objectParameters.addParamString("Text", cLabel, &iemHelper.labelText, ""); objectParameters.addParamColourLabel(&iemHelper.labelColour); - objectParameters.addParamInt("Label X", cLabel, &iemHelper.labelX, 0); - objectParameters.addParamInt("Label Y", cLabel, &iemHelper.labelY, -8); + objectParameters.addParamRange("Position", cLabel, &iemHelper.labelPosition, {0, -8}); objectParameters.addParamBool("Initialise", cGeneral, &iemHelper.initialise, { "No", "Yes" }, 0); input.setResetValue(0.0f); diff --git a/Source/Objects/ObjectParameters.h b/Source/Objects/ObjectParameters.h index 4e9fb194d..7c53a7649 100644 --- a/Source/Objects/ObjectParameters.h +++ b/Source/Objects/ObjectParameters.h @@ -111,17 +111,17 @@ class ObjectParameters { void addParamColourFG(Value* pVal) { - objectParameters.add(makeParam("Foreground color", tColour, cAppearance, pVal, StringArray(), PlugDataColour::canvasTextColourId)); + objectParameters.add(makeParam("Foreground", tColour, cAppearance, pVal, StringArray(), PlugDataColour::canvasTextColourId)); } void addParamColourBG(Value* pVal) { - objectParameters.add(makeParam("Background color", tColour, cAppearance, pVal, StringArray(), PlugDataColour::guiObjectBackgroundColourId)); + objectParameters.add(makeParam("Background", tColour, cAppearance, pVal, StringArray(), PlugDataColour::guiObjectBackgroundColourId)); } void addParamColourLabel(Value* pVal) { - objectParameters.add(makeParam("Label color", tColour, cLabel, pVal, StringArray(), PlugDataColour::canvasTextColourId)); + objectParameters.add(makeParam("Color", tColour, cLabel, pVal, StringArray(), PlugDataColour::canvasTextColourId)); } void addParamReceiveSymbol(Value* pVal) diff --git a/Source/Sidebar/Inspector.h b/Source/Sidebar/Inspector.h index 62cba05f6..f7d7aeae0 100644 --- a/Source/Sidebar/Inspector.h +++ b/Source/Sidebar/Inspector.h @@ -90,7 +90,7 @@ class Inspector : public Component { Inspector() : redirector(this) { - panel.setTitleHeight(20); + panel.setTitleHeight(22); panel.setTitleAlignment(PropertiesPanel::AlignWithPropertyName); panel.setDrawShadowAndOutline(false); addAndMakeVisible(panel); @@ -101,6 +101,7 @@ class Inspector : public Component { { panel.setSeparatorColour(PlugDataColour::sidebarBackgroundColourId); panel.setPanelColour(PlugDataColour::sidebarActiveBackgroundColourId); + panel.setPanelAlpha(0.75f); } void paint(Graphics& g) override @@ -110,7 +111,7 @@ class Inspector : public Component { void resized() override { - panel.setBounds(getLocalBounds()); + panel.setBounds(getLocalBounds().withTrimmedTop(2)); resetButton.setTopLeftPosition(getLocalBounds().withTrimmedRight(23).getRight(), 0); panel.setContentWidth(getWidth() - 16); @@ -127,9 +128,9 @@ class Inspector : public Component { case tInt: return new PropertiesPanel::EditableComponent(name, *value, 0.0f, 0.0f, onInteractionFn); case tColour: - return new PropertiesPanel::ColourComponent(name, *value); + return new PropertiesPanel::InspectorColourComponent(name, *value); case tBool: - return new PropertiesPanel::BoolComponent(name, *value, options); + return new PropertiesPanel::InspectorBoolComponent(name, *value, options); case tCombo: return new PropertiesPanel::ComboComponent(name, *value, options); case tRangeFloat: @@ -212,12 +213,12 @@ class Inspector : public Component { else if (objectParameters.size() == 1) { auto newPanel = createPanel(type, name, value, options, onInteractionFn); - newPanel->setPreferredHeight(26); + newPanel->setPreferredHeight(32); panels.add(newPanel); } else { auto* redirectedProperty = redirector.addProperty(value, otherValues); auto newPanel = createPanel(type, name, redirectedProperty, options); - newPanel->setPreferredHeight(26); + newPanel->setPreferredHeight(32); panels.add(newPanel); } }