Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Style component to display Juce build-in widget Styles: i.e. SliderType, TextPosition #129

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions melatonin/component_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace melatonin
juce::Value timing1, timing2, timing3, timingMax, hasChildren;

juce::Value isToggleable, toggleState, clickTogglesState, radioGroupId;
juce::Value sliderStyleValue, sliderTextBoxPositionValue, buttonStyleValue, comboBoxTextEditableValue;

struct AccessiblityDetail
{
Expand Down Expand Up @@ -79,8 +80,69 @@ namespace melatonin
juce::Value value;
};

enum class StyleType
{
SliderStyle,
SliderTextPosition,
ButtonStyle,
ComboBoxEditable
};

struct StyleProperty
{
StyleProperty() = default;
StyleProperty(StyleType t, const juce::var& v, juce::StringArray opts = {})
: type(t), value(v), options(std::move(opts))
{
}

StyleType type;
juce::Value value;
juce::StringArray options; // For choice/enum properties

// Helper to get display name
[[nodiscard]] juce::String getDisplayName() const
{
switch (type)
{
case StyleType::SliderStyle: return "Slider Style";
case StyleType::SliderTextPosition: return "Text Position";
case StyleType::ButtonStyle: return "Button Style";
case StyleType::ComboBoxEditable: return "Text Editable";
default: return "Unknown";
}
}

// Helper to get options for the style type
static juce::StringArray getOptionsForType(StyleType t)
{
switch (t)
{
case StyleType::SliderStyle:
return {"LinearHorizontal", "LinearVertical", "LinearBar",
"Rotary", "RotaryHorizontalDrag", "RotaryVerticalDrag",
"IncDecButtons", "TwoValueHorizontal", "TwoValueVertical"};

case StyleType::SliderTextPosition:
return {"NoTextBox", "TextBoxLeft", "TextBoxRight",
"TextBoxAbove", "TextBoxBelow"};

case StyleType::ButtonStyle:
return {"TextButton", "DrawableButton", "ImageButton",
"ArrowButton", "HyperlinkButton"};

case StyleType::ComboBoxEditable:
return {"NotEditable", "Editable"};

default:
return {};
}
}
};

std::vector<NamedProperty> namedProperties;
std::vector<NamedProperty> colors;
std::vector<StyleProperty> styles;

void refresh()
{
Expand Down Expand Up @@ -142,6 +204,8 @@ namespace melatonin
typeValue = type (*selectedComponent);
accessibilityHandledValue = selectedComponent->isAccessible();

updateStyleProperties();

if (auto button = dynamic_cast<juce::Button*> (selectedComponent.getComponent()))
{
isToggleable = button->isToggleable();
Expand Down Expand Up @@ -169,6 +233,11 @@ namespace melatonin
clickTogglesState.addListener (this);
radioGroupId.addListener (this);

sliderStyleValue.addListener (this);
sliderTextBoxPositionValue.addListener (this);
buttonStyleValue.addListener (this);
comboBoxTextEditableValue.addListener (this);

if (selectedComponent->isAccessible() && selectedComponent->getAccessibilityHandler())
{
auto* accH = selectedComponent->getAccessibilityHandler();
Expand Down Expand Up @@ -254,10 +323,42 @@ namespace melatonin

for (auto& nv : colors)
nv.value.addListener (this);

for (auto& style : styles)
style.value.addListener (this);
}
notifyListeners();
}

void updateStyleProperties()
{
if (auto* slider = dynamic_cast<juce::Slider*>(selectedComponent.getComponent()))
{
styles.emplace_back(
StyleType::SliderStyle,
static_cast<int>(slider->getSliderStyle()),
StyleProperty::getOptionsForType(StyleType::SliderStyle));

styles.emplace_back(
StyleType::SliderTextPosition,
static_cast<int>(slider->getTextBoxPosition()),
StyleProperty::getOptionsForType(StyleType::SliderTextPosition));
}
else if (auto* button = dynamic_cast<juce::Button*>(selectedComponent.getComponent()))
{
styles.emplace_back(
StyleType::ButtonStyle,
button->getButtonText().isEmpty() ? 1 : 0,
StyleProperty::getOptionsForType(StyleType::ButtonStyle));
}
else if (auto* comboBox = dynamic_cast<juce::ComboBox*>(selectedComponent.getComponent()))
{
styles.emplace_back(
StyleType::ComboBoxEditable,
comboBox->isTextEditable());
}
}

void removeListeners()
{
TRACE_COMPONENT();
Expand All @@ -280,14 +381,23 @@ namespace melatonin
clickTogglesState.removeListener (this);
radioGroupId.removeListener (this);

sliderStyleValue.removeListener (this);
sliderTextBoxPositionValue.removeListener (this);
buttonStyleValue.removeListener (this);
comboBoxTextEditableValue.removeListener (this);

for (auto& np : namedProperties)
np.value.removeListener (this);

for (auto& np : colors)
np.value.removeListener (this);

for (auto& style : styles)
style.value.removeListener (this);

colors.clear();
namedProperties.clear();
styles.clear();
}

// allows properties to be set from our properties
Expand Down Expand Up @@ -382,6 +492,43 @@ namespace melatonin
break;
}
}

for (auto& style : styles)
{
if (value.refersToSameSourceAs(style.value))
{
switch (style.type)
{
case StyleType::SliderStyle:
if (auto* slider = dynamic_cast<juce::Slider*>(selectedComponent.getComponent()))
slider->setSliderStyle(static_cast<juce::Slider::SliderStyle>(int(style.value.getValue())));
break;

case StyleType::SliderTextPosition:
if (auto* slider = dynamic_cast<juce::Slider*>(selectedComponent.getComponent()))
slider->setTextBoxStyle(
static_cast<juce::Slider::TextEntryBoxPosition>(int(style.value.getValue())),
false,
slider->getTextBoxWidth(),
slider->getTextBoxHeight());
break;

case StyleType::ButtonStyle:
if (auto* button = dynamic_cast<juce::Button*>(selectedComponent.getComponent()))
{
// Handle button style changes
selectedComponent->repaint();
}
break;

case StyleType::ComboBoxEditable:
if (auto* comboBox = dynamic_cast<juce::ComboBox*>(selectedComponent.getComponent()))
comboBox->setEditableText(style.value.getValue());
break;
}
return; // Style was handled, exit early
}
}
}
}
}
Expand Down
106 changes: 106 additions & 0 deletions melatonin/components/style_component.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#pragma once
#include "melatonin_inspector/melatonin/component_model.h"

namespace melatonin
{
class StyleProperties : public juce::Component, private ComponentModel::Listener
{
public:
explicit StyleProperties(ComponentModel& _model) : model(_model)
{
reset();

addAndMakeVisible(&panel);
model.addListener(*this);
}

~StyleProperties() override
{
model.removeListener(*this);
}

void resized() override
{
TRACE_COMPONENT();
panel.setBounds(getLocalBounds().withTrimmedTop(padding));
}

void reset()
{
updateProperties();
resized();
}

private:
ComponentModel& model;
juce::PropertyPanel panel{"Style Properties"};
int padding = 3;

void componentModelChanged(ComponentModel&) override
{
updateProperties();
}

void updateProperties()
{
TRACE_COMPONENT();
panel.clear();

if (!model.getSelectedComponent())
return;

auto props = createStylePropertyComponents();
for (auto* p : props)
{
p->setLookAndFeel(&getLookAndFeel());
}
panel.addProperties(props, padding);

resized();
}

[[nodiscard]] juce::Array<juce::PropertyComponent*> createStylePropertyComponents() const
{
TRACE_COMPONENT();
juce::Array<juce::PropertyComponent*> props;

for (const auto& style : model.styles)
{
if (!style.options.isEmpty())
{
// For enumerated/choice properties
juce::Array<juce::var> optionIndices;
for (int i = 0; i < style.options.size(); ++i)
optionIndices.add(i);
auto choiceComp = new juce::ChoicePropertyComponent(
style.value,
style.getDisplayName(),
style.options,
optionIndices);
props.add(choiceComp);
}
else if (style.value.getValue().isBool())
{
// For boolean properties
props.add(new juce::BooleanPropertyComponent(
style.value,
style.getDisplayName(),
""));
}
else
{
// For text/numeric properties
props.add(new juce::TextPropertyComponent(
style.value,
style.getDisplayName(),
50,
false));
}
}

return props;
}

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StyleProperties)
};
}
16 changes: 16 additions & 0 deletions melatonin/inspector_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#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/style_component.h"
#include "melatonin_inspector/melatonin/lookandfeel.h"

/*
Expand Down Expand Up @@ -42,13 +43,15 @@ namespace melatonin
addChildComponent (colorPicker);
addChildComponent (preview);
addChildComponent (properties);
addChildComponent (styleProperties);
addChildComponent (accessibility);

// z-order on panels is higher so they are clickable
addAndMakeVisible (boxModelPanel);
addAndMakeVisible (colorPickerPanel);
addAndMakeVisible (previewPanel);
addAndMakeVisible (propertiesPanel);
addAndMakeVisible (stylePanel);
addAndMakeVisible (accessibilityPanel);

addAndMakeVisible (searchBox);
Expand Down Expand Up @@ -273,6 +276,14 @@ namespace melatonin
colorPicker.setBounds (colorPickerBounds.withTrimmedLeft (32));
colorPickerPanel.setBounds (colorPickerBounds.removeFromTop (32).removeFromLeft (200));

int numOfStyleToDisplay = juce::jlimit (0, 5, (int) model.styles.size());
auto stylePropertiesHeight = 32;
if (styleProperties.isVisible() && !model.styles.empty())
stylePropertiesHeight += 32 * numOfStyleToDisplay;
auto stylePropertiesBounds = mainCol.removeFromTop (stylePropertiesHeight);
stylePanel.setBounds (stylePropertiesBounds.removeFromTop (32).removeFromLeft (200));
styleProperties.setBounds (stylePropertiesBounds.withTrimmedLeft (32));

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

Expand Down Expand Up @@ -347,6 +358,7 @@ namespace melatonin
tree.clearSelectedItems();

properties.reset();
styleProperties.reset();
model.deselectComponent();
tree.setRootItem (getRoot());

Expand All @@ -370,6 +382,7 @@ namespace melatonin
previewPanel.setVisible (nowEnabled);
colorPickerPanel.setVisible (nowEnabled);
propertiesPanel.setVisible (nowEnabled);
stylePanel.setVisible(nowEnabled);
tree.setVisible (nowEnabled);

if (!nowEnabled)
Expand Down Expand Up @@ -419,6 +432,9 @@ namespace melatonin
Properties properties { model };
CollapsablePanel propertiesPanel { "PROPERTIES", &properties, true };

StyleProperties styleProperties { model };
CollapsablePanel stylePanel { "STYLE", &styleProperties, false };

Accessibility accessibility { model };
CollapsablePanel accessibilityPanel { "ACCESSIBILITY", &accessibility, false };

Expand Down
Loading
Loading