#include "SkNormalSource.h"
#include "sk_tool_utils.h"
+class ParentControl;
+
+// Abstract base class for all components that a control panel must have
+class Control : public SkRefCnt {
+public:
+ Control(SkString name)
+ : fName(name)
+ , fParent(nullptr)
+ , fRelativePos(SkPoint::Make(0.0f, 0.0f)) {}
+
+ // Use this to propagate a click's position down to a control. Gets modulated by the component's
+ // relative position
+ bool click(const SkPoint& clickPos) {
+ SkPoint relativeClickPos = SkPoint::Make(clickPos.fX - fRelativePos.fX,
+ clickPos.fY - fRelativePos.fY);
+ return this->onClick(relativeClickPos);
+ }
+
+ // Use this to draw the control and its appropriate children. Gets modulated by the component's
+ // relative position.
+ void drawContent(SkCanvas *canvas) {
+ canvas->save();
+ canvas->translate(fRelativePos.fX, fRelativePos.fY);
+ this->onDrawContent(canvas);
+ canvas->restore();
+ }
+
+ /* Returns true when click position argumend lands over a control region in this control. Click
+ * position gets modulated by the component's relative position.
+ *
+ * @param click The position of the click in the coordinate space relative to the parent
+ */
+ bool isInCtrlRegion(const SkPoint& click) {
+ SkPoint relativeClickPos = SkPoint::Make(click.fX - fRelativePos.fX,
+ click.fY - fRelativePos.fY);
+ return this->onIsInCtrlRegion(relativeClickPos);
+ }
+
+ // Returns height of content drawn
+ virtual SkScalar height() const = 0;
+
+ // Sets the parent of this component. May only be used once. Height must remain constant after
+ // parent is set.
+ void setParent(ParentControl *parent, const SkPoint& relativePos) {
+ SkASSERT(parent);
+ SkASSERT(!fParent); // No chidren transfer since relativeY would get invalid for younger kid
+
+ fParent = parent;
+ fRelativePos = relativePos;
+ this->onSetParent();
+ }
+
+ // Overriden by sub-classes that need to recompute fields after parent is set. Called after
+ // setting fParent.
+ virtual void onSetParent() {}
+
+ // Overriden by sub-classes that need to know when a click is released.
+ virtual void onClickRelease() {}
+
+protected:
+
+ // Draws a label for the component, using its name and a passed value. Does NOT modulate by
+ // relative height, expects CTM to have been adjusted in advance.
+ void drawLabel(SkCanvas *canvas, const SkString& valueStr) const {
+ // TODO Cache this
+ sk_sp<SkTypeface> fLabelTypeface =
+ sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
+
+ SkString label;
+ label.append(fName);
+ label.append(": ");
+ label.append(valueStr);
+
+ SkPaint labelPaint;
+ labelPaint.setTypeface(fLabelTypeface);
+ labelPaint.setAntiAlias(true);
+ labelPaint.setColor(0xFFFFFFFF);
+ labelPaint.setTextSize(12.0f);
+
+ canvas->drawText(label.c_str(), label.size(), 0, kLabelHeight - 6.0f, labelPaint);
+ }
+
+ SkString fName;
+ ParentControl* fParent;
+
+ static constexpr SkScalar kLabelHeight = 20.0f;
+
+private:
+ // Overriden by sub-class to draw component. Do not call directly, drawContent() modulates by
+ // relative position.
+ virtual void onDrawContent(SkCanvas *canvas) = 0;
+
+ // Overriden by sub-class to handle clicks. Do not call directly, click() modulates by relative
+ // position. Return true if holding mouse capture
+ virtual bool onClick(const SkPoint& clickPos) { return false; };
+
+ // Overriden by sub-classes with controls. Should return true if clickPos lands inside a control
+ // region, to enable mouse caputre.
+ virtual bool onIsInCtrlRegion(const SkPoint& clickPos) const { return false; };
+
+ // The position of the control relative to it's parent
+ SkPoint fRelativePos;
+};
+
+class ParentControl : public Control { // Interface for all controls that have children
+public:
+ ParentControl(const SkString& name) : INHERITED(name) {}
+
+ // Adds a child
+ virtual void add(sk_sp<Control> control) = 0;
+
+ // Returns the control's width. Used to propagate width down to components that don't specify it
+ virtual SkScalar width() const = 0;
+
+private:
+ typedef Control INHERITED;
+};
+
+class ControlPanel : public ParentControl {
+public:
+
+ ControlPanel(SkScalar width)
+ : ParentControl(SkString("ControlPanel"))
+ , fWidth(width)
+ , fHeight(0.0f)
+ , fSelectedControl(-1) {}
+
+ // Width unspecified, expectation is inheritance from parent
+ ControlPanel() : ControlPanel(-1.0f) {}
+
+ // Use this for introducing clicks on a ControlPanel from outside of the framework. It
+ // propagates click release or position down the chain. Returns false when click capture is
+ // being released.
+ bool inClick(SkView::Click *inClick) {
+ if (SkView::Click::State::kUp_State == inClick->fState) {
+ this->onClickRelease();
+ return false;
+ }
+ return this->click(inClick->fCurr);
+ }
+
+ // Add children
+ void add(sk_sp<Control> control) override {
+ SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
+ fControls.push_back(control);
+ control->setParent(this, SkPoint::Make(0.0f, fHeight));
+ fHeight += control->height();
+ }
+
+ SkScalar width() const override {
+ return fParent ? fParent->width() : fWidth; // Width inherited from parent if there is one
+ }
+
+ SkScalar height() const override {
+ return fHeight;
+ }
+
+ // Propagate click release to selected control, deselect control
+ void onClickRelease() override {
+ if (fSelectedControl >= 0) {
+ fControls[fSelectedControl]->onClickRelease();
+ }
+ fSelectedControl = -1;
+ }
+
+ // Propagate onSetParent() down to children, some might need fParent->width() refresh
+ void onSetParent() override {
+ for (int i = 0; i < fControls.count(); i++) {
+ fControls[i]->onSetParent();
+ }
+ }
+
+ // Holds a vertical shelf of controls. Can't be hierarchy root if not given a width value.
+ static sk_sp<ParentControl> Make() {
+ return sk_sp<ParentControl>(new ControlPanel());
+ }
+
+ // Holds a vertical shelf of controls. Only control that can be hooked from outside the
+ // framework.
+ static sk_sp<ParentControl> Make(SkScalar width) {
+ return sk_sp<ParentControl>(new ControlPanel(width));
+ }
+
+protected:
+ // Returns true if control panel has mouse captured, false when it is ready to release
+ // capture
+ bool onClick(const SkPoint& click) override {
+
+ if (fSelectedControl == -1) { // If no child control selected, check every child
+ for (int i = 0; i < fControls.count(); i++) {
+ if (fControls[i]->isInCtrlRegion(click)) {
+ fSelectedControl = i;
+ break;
+ }
+ }
+ }
+
+ if (fSelectedControl >= 0) { // If child control selected, propagate click
+ bool keepSelection = fControls[fSelectedControl]->click(click);
+ if (!keepSelection) {
+ fSelectedControl = -1;
+ }
+ return keepSelection;
+ }
+
+ return false;
+ }
+
+ // Draw all children
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->save();
+ for (int i = 0; i < fControls.count(); i++) {
+ fControls[i]->drawContent(canvas);
+ }
+ canvas->restore();
+ }
+
+ // Check all children's control regions
+ bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
+ for (int i = 0; i < fControls.count(); i++) {
+ if (fControls[i]->isInCtrlRegion(clickPos)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+private:
+ SkScalar fWidth;
+ SkScalar fHeight;
+
+ SkTArray<sk_sp<Control>> fControls;
+ int fSelectedControl;
+};
+
+class DiscreteSliderControl : public Control {
+public:
+ SkScalar height() const override {
+ return 2.0f * kLabelHeight;
+ }
+
+ // Set width-dependant variables when new parent is set
+ void onSetParent() override {
+ fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
+ fSliderRange = fParent->width() - kSliderWidth;
+ }
+
+ /* Make a slider for an integer value. Snaps to discrete positions.
+ *
+ * @params name The name of the control, displayed in the label
+ * @params output Pointer to the integer that will be set by the slider
+ * @params min Min value for output.
+ * @params max Max value for output.
+ */
+ static sk_sp<Control> Make(SkString name, int* output, int min, int max) {
+ return sk_sp<Control>(new DiscreteSliderControl(name, output, min, max));
+ }
+
+protected:
+ void onDrawContent(SkCanvas* canvas) override {
+ SkASSERT(fParent);
+ int numChoices = fMax - fMin + 1;
+ fSlider.offsetTo(fSliderRange * ( (*fOutput)/SkIntToScalar(numChoices)
+ + 1.0f/(2.0f * numChoices) ),
+ fSlider.fTop);
+
+ SkString valueStr;
+ valueStr.appendS32(*fOutput);
+ this->drawLabel(canvas, valueStr);
+
+ SkPaint sliderPaint;
+ sliderPaint.setColor(0xFFF3F3F3);
+ canvas->drawRect(fSlider, sliderPaint);
+
+ SkPaint ctrlRegionPaint;
+ ctrlRegionPaint.setColor(0xFFFFFFFF);
+ ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
+ ctrlRegionPaint.setStrokeWidth(2.0f);
+ canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
+ }
+
+ bool onClick(const SkPoint& clickPos) override {
+ SkASSERT(fParent);
+ SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
+ int numChoices = fMax - fMin + 1;
+ *fOutput = SkTMin(SkScalarFloorToInt(numChoices * x / fSliderRange) + fMin, fMax);
+
+ return true;
+ }
+
+ bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
+ SkASSERT(fParent);
+ return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
+ }
+
+private:
+ DiscreteSliderControl(SkString name, int* output, int min, int max)
+ : INHERITED(name)
+ , fOutput(output)
+ , fMin(min)
+ , fMax(max) {
+ fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
+ }
+
+ int* fOutput;
+ int fMin;
+ int fMax;
+ SkRect fSlider; // The rectangle that slides
+ // The region in which the rectangle slides. Also the region in which mouse is caputred
+ SkRect fCtrlRegion;
+ SkScalar fSliderRange; // The width in pixels over which the slider can slide
+
+ static constexpr SkScalar kSliderHeight = 20.0f;
+ static constexpr SkScalar kSliderWidth = 10.0f;
+
+ typedef Control INHERITED;
+};
+
+class ControlSwitcher : public ParentControl {
+public:
+ // Add children
+ void add(sk_sp<Control> control) override {
+ SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
+ fControls.push_back(control);
+ control->setParent(this, SkPoint::Make(0.0f, kSelectorHeight));
+ fHeight = SkMaxScalar(fHeight, control->height()); // Setting height to max child height.
+ }
+
+ SkScalar width() const override { return fParent ? (fParent->width()) : 0; }
+
+ SkScalar height() const override {
+ return fHeight;
+ }
+
+ // Propagate onClickRelease to control that currently captures mouse
+ void onClickRelease() override {
+ if (fCtrlOnClick) {
+ fCtrlOnClick->onClickRelease();
+ }
+ fCtrlOnClick = nullptr;
+ }
+
+ void onSetParent() override {
+ for (int i = 0; i < fControls.count(); i++) {
+ fControls[i]->onSetParent(); // Propagate to children
+ }
+
+ // Finalize control selector
+ // TODO can be moved to constructor if list-initialized
+ if (!finalizedChildren) {
+ fControlSelector = DiscreteSliderControl::Make(
+ SkString(fName), &fSelectedControl, 0, fControls.count()-1);
+ fControlSelector->setParent(this, SkPoint::Make(0.0f, 0.0f));
+ fHeight += kSelectorHeight;
+
+ SkASSERT(fControlSelector->height() <= kSelectorHeight);
+ }
+ }
+
+ /* A set of a selector and a list of controls. Displays the control from the list of controls
+ * with the index set by the aforementioned selector.
+ *
+ * @param name The name of the switcher. Will be displayed in the selector's label.
+ */
+ static sk_sp<ParentControl> Make(const SkString& name) {
+ return sk_sp<ParentControl>(new ControlSwitcher(name));
+ }
+
+protected:
+ // Draw selector and currently selected control
+ void onDrawContent(SkCanvas* canvas) override {
+ fControlSelector->drawContent(canvas);
+ fControls[fSelectedControl]->drawContent(canvas);
+ }
+
+ // Returns true if control panel has mouse captured, false when it is ready to release
+ // capture
+ bool onClick(const SkPoint& click) override {
+ if (!fCtrlOnClick) {
+ if (fControlSelector->isInCtrlRegion(click)) {
+ fCtrlOnClick = fControlSelector.get();
+ } else if (fControls[fSelectedControl]->isInCtrlRegion(click)) {
+ fCtrlOnClick = fControls[fSelectedControl].get();
+ }
+ }
+ if (fCtrlOnClick) {
+ return fCtrlOnClick->click(click);
+ }
+
+ return false;
+ }
+
+ // Is in control region of selector or currently selected control
+ bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
+ if (fControlSelector->isInCtrlRegion(clickPos)) {
+ return true;
+ }
+ if (fControls[fSelectedControl]->isInCtrlRegion(clickPos)) {
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ ControlSwitcher(const SkString& name)
+ : INHERITED(name)
+ , fHeight(0.0)
+ , fSelectedControl(0)
+ , fCtrlOnClick(nullptr){}
+
+ bool finalizedChildren = false;
+
+ sk_sp<Control> fControlSelector;
+ SkScalar fHeight;
+ SkTArray<sk_sp<Control>> fControls;
+ int fSelectedControl;
+
+ Control* fCtrlOnClick;
+
+ static constexpr SkScalar kSelectorHeight = 40.0f;
+
+ typedef ParentControl INHERITED;
+};
+
+class ContinuousSliderControl : public Control {
+public:
+ SkScalar height() const override {
+ return 2.0f * kLabelHeight;
+ }
+
+ void onSetParent() override {
+ fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
+ fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
+ fSliderRange = fParent->width() - kSliderWidth;
+ }
+
+ /* Make a slider for an SkScalar.
+ *
+ * @params name The name of the control, displayed in the label
+ * @params output Pointer to the SkScalar that will be set by the slider
+ * @params min Min value for output
+ * @params max Max value for output
+ */
+ static sk_sp<Control> Make(const SkString& name, SkScalar* output, SkScalar min, SkScalar max) {
+ return sk_sp<Control>(new ContinuousSliderControl(name, output, min, max));
+ }
+
+protected:
+ void onDrawContent(SkCanvas* canvas) override {
+ SkASSERT(fParent);
+ SkScalar x = fSliderRange * (*fOutput - fMin) / (fMax - fMin);
+ fSlider.offsetTo(SkScalarPin(x, 0.0f, fSliderRange), fSlider.fTop);
+
+ SkString valueStr;
+ valueStr.appendScalar(*fOutput);
+ this->drawLabel(canvas, valueStr);
+
+ SkPaint sliderPaint;
+ sliderPaint.setColor(0xFFF3F3F3);
+ canvas->drawRect(fSlider, sliderPaint);
+
+ SkPaint ctrlRegionPaint;
+ ctrlRegionPaint.setColor(0xFFFFFFFF);
+ ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
+ ctrlRegionPaint.setStrokeWidth(2.0f);
+ canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
+ }
+
+ bool onClick(const SkPoint& clickPos) override {
+ SkASSERT(fParent);
+ SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
+ *fOutput = (x/fSliderRange) * (fMax - fMin) + fMin;
+ return true;
+ }
+
+ bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
+ SkASSERT(fParent);
+ return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
+ }
+
+private:
+ ContinuousSliderControl(const SkString& name, SkScalar* output, SkScalar min, SkScalar max)
+ : INHERITED(name)
+ , fOutput(output)
+ , fMin(min)
+ , fMax(max) {}
+
+ SkScalar* fOutput;
+ SkScalar fMin;
+ SkScalar fMax;
+ SkRect fSlider;
+ SkRect fCtrlRegion;
+ SkScalar fSliderRange;
+
+ static constexpr SkScalar kSliderHeight = 20.0f;
+ static constexpr SkScalar kSliderWidth = 10.0f;
+
+ typedef Control INHERITED;
+};
+
+class RadialDirectionControl : public Control {
+public:
+ SkScalar height() const override {
+ return kLabelHeight + 2.0f * kRegionRadius;
+ }
+
+ /* Make a direction selector.
+ *
+ * @params name The name of the control, displayed in the label
+ * @params output Pointer to the SkVector that will be set by the slider
+ */
+ static sk_sp<Control> Make(const SkString& name, SkVector* output) {
+ return sk_sp<Control>(new RadialDirectionControl(name, output));
+ }
+
+protected:
+ void onDrawContent(SkCanvas* canvas) override {
+ SkASSERT(fParent);
+
+ SkString valueStr;
+ valueStr.appendf("%.2f, %.2f", fOutput->fX, fOutput->fY);
+ this->drawLabel(canvas, valueStr);
+
+ SkPoint lineEnd = SkPoint::Make(fCtrlRegion.centerX(), fCtrlRegion.centerY())
+ + (*fOutput * (kRegionRadius - kCapRadius));
+ SkPaint linePaint;
+ linePaint.setColor(0xFFF3F3F3);
+ linePaint.setStrokeWidth(kStrokeWidth);
+ linePaint.setAntiAlias(true);
+ linePaint.setStrokeCap(SkPaint::kRound_Cap);
+ canvas->drawLine(fCtrlRegion.centerX(), fCtrlRegion.centerY(),
+ lineEnd.fX, lineEnd.fY, linePaint);
+
+ SkPaint ctrlRegionPaint;
+ ctrlRegionPaint.setColor(0xFFFFFFFF);
+ ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
+ ctrlRegionPaint.setStrokeWidth(2.0f);
+ ctrlRegionPaint.setAntiAlias(true);
+ canvas->drawCircle(fCtrlRegion.centerX(), fCtrlRegion.centerY(), kRegionRadius,
+ ctrlRegionPaint);
+ }
+
+ bool onClick(const SkPoint& clickPos) override {
+ SkASSERT(fParent);
+ fOutput->fX = clickPos.fX - fCtrlRegion.centerX();
+ fOutput->fY = clickPos.fY - fCtrlRegion.centerY();
+ fOutput->normalize();
+
+ return true;
+ }
+
+ bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
+ SkASSERT(fParent);
+ return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY,
+ 1, 1));
+ }
+
+private:
+ RadialDirectionControl(const SkString& name, SkVector* output)
+ : INHERITED(name)
+ , fOutput(output) {
+ fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight,
+ kRegionRadius * 2.0f, kRegionRadius * 2.0f);
+ }
+
+ SkVector* fOutput;
+ SkRect fCtrlRegion;
+
+ static constexpr SkScalar kRegionRadius = 50.0f;
+ static constexpr SkScalar kStrokeWidth = 6.0f;
+ static constexpr SkScalar kCapRadius = kStrokeWidth / 2.0f;
+
+ typedef Control INHERITED;
+};
+
+class ColorDisplay: public Control {
+public:
+ SkScalar height() const override {
+ return kHeight;
+ }
+
+ void onSetParent() override {
+ fDisplayRect = SkRect::MakeXYWH(0.0f, kPadding, fParent->width(), kHeight - kPadding);
+ }
+
+ /* Make a display that shows an SkColor3f.
+ *
+ * @params output Pointer to the SkColor3f that will be displayed
+ */
+ static sk_sp<Control> Make(SkColor3f* input) {
+ return sk_sp<Control>(new ColorDisplay(SkString("ColorDisplay"), input));
+ }
+
+protected:
+ void onDrawContent(SkCanvas* canvas) override {
+ SkASSERT(fParent);
+
+ SkPaint displayPaint;
+ displayPaint.setColor(SkColor4f::FromColor3f(*fInput, 1.0f).toSkColor());
+ canvas->drawRect(fDisplayRect, displayPaint);
+ }
+
+private:
+ ColorDisplay(const SkString& name, SkColor3f* input)
+ : INHERITED(name)
+ , fInput(input) {}
+
+ SkColor3f* fInput;
+ SkRect fDisplayRect;
+
+ static constexpr SkScalar kHeight = 24.0f;
+ static constexpr SkScalar kPadding = 4.0f;
+
+ typedef Control INHERITED;
+};
class BevelView : public SampleView {
public:
BevelView()
: fShapeBounds(SkRect::MakeWH(kShapeBoundsSize, kShapeBoundsSize))
- , fRedLight(SkLights::Light::MakeDirectional(SkColor3f::Make(0.6f, 0.45f, 0.3f),
- SkVector3::Make(0.0f, -5.0f, 1.0f)))
- , fBlueLight(SkLights::Light::MakeDirectional(SkColor3f::Make(0.3f, 0.45f, 0.6f),
- SkVector3::Make(0.0f, 5.0f, 1.0f))) {
+ , fControlPanel(kCtrlRange) {
this->setBGColor(0xFF666868); // Slightly colorized gray for contrast
- // Lights
- SkLights::Builder builder;
- builder.add(fRedLight);
- builder.add(fBlueLight);
- builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.4f, 0.4f, 0.4f)));
- fLights = builder.finish();
-
// Controls
+ fBevelWidth = 25.0f;
+ fBevelHeight = 25.0f;
+ fBevelType = 0;
+
+ int currLight = 0;
+ fLightDefs[currLight++] =
+ {SkVector::Make(0.0f, 1.0f), 1.0f, SkColor3f::Make(0.6f, 0.45f, 0.3f)};
+ fLightDefs[currLight++] =
+ {SkVector::Make(0.0f, -1.0f), 1.0f, SkColor3f::Make(0.3f, 0.45f, 0.6f)};
+ fLightDefs[currLight++] =
+ {SkVector::Make(1.0f, 0.0f), 1.0f, SkColor3f::Make(0.0f, 0.0f, 0.0f)};
+ // Making sure we initialized all lights
+ SkASSERT(currLight == kNumLights);
+
+ fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelWidth"), &fBevelWidth,
+ 1.0f, kShapeBoundsSize));
+ fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelHeight"), &fBevelHeight,
+ -50.0f, 50.0f));
+ fControlPanel.add(DiscreteSliderControl::Make(SkString("BevelType"), &fBevelType,
+ 0, 2));
+ sk_sp<ParentControl> lightCtrlSelector = ControlSwitcher::Make(SkString("SelectedLight"));
+ for (int i = 0; i < kNumLights; i++) {
+ SkString name("Light");
+ name.appendS32(i);
+ sk_sp<ParentControl> currLightPanel = ControlPanel::Make();
+ SkString dirName(name);
+ dirName.append("Dir");
+ currLightPanel->add(RadialDirectionControl::Make(dirName, &(fLightDefs[i].fDirXY)));
+ SkString heightName(name);
+ heightName.append("Height");
+ currLightPanel->add(ContinuousSliderControl::Make(heightName, &(fLightDefs[i].fDirZ),
+ 0.0f, 2.0f));
+ SkString redName(name);
+ redName.append("Red");
+ currLightPanel->add(ContinuousSliderControl::Make(redName, &(fLightDefs[i].fColor.fX),
+ 0.0f, 1.0f));
+ SkString greenName(name);
+ greenName.append("Green");
+ currLightPanel->add(ContinuousSliderControl::Make(greenName, &(fLightDefs[i].fColor.fY),
+ 0.0f, 1.0f));
+ SkString blueName(name);
+ blueName.append("Blue");
+ currLightPanel->add(ContinuousSliderControl::Make(blueName, &(fLightDefs[i].fColor.fZ),
+ 0.0f, 1.0f));
+ currLightPanel->add(ColorDisplay::Make(&(fLightDefs[i].fColor)));
+ lightCtrlSelector->add(currLightPanel);
+ }
+ fControlPanel.add(lightCtrlSelector);
- SkScalar currY = kSliderHeight;
-
- const SkScalar kWidthCtrlInitialPos = 0.2f;
- fCtrlRangeRects[0] = SkRect::MakeXYWH(0.0f, currY,
- kCtrlRange + kSliderWidth,
- kSliderHeight);
- fWidthCtrlRect = SkRect::MakeXYWH(kWidthCtrlInitialPos * kCtrlRange, currY,
- kSliderWidth, kSliderHeight);
- fBevelWidth = kBevelWidthMax * kWidthCtrlInitialPos;
- currY += 2 * kSliderHeight;
-
- const SkScalar kHeightCtrlInitialPos = 0.75f;
- fCtrlRangeRects[1] = SkRect::MakeXYWH(0.0f, currY,
- kCtrlRange + kSliderWidth,
- kSliderHeight);
- fHeightCtrlRect = SkRect::MakeXYWH(kHeightCtrlInitialPos * kCtrlRange, currY,
- kSliderWidth, kSliderHeight);
- // Mapping from (0, 1) to (-1, 1)
- fBevelHeight = kBevelHeightMax * (kHeightCtrlInitialPos * 2.0f - 1.0f);
- currY += 2 * kSliderHeight;
-
- const SkScalar kTypeCtrlInitialPos = 1.0f / (2.0f * kBevelTypeCount);
- fCtrlRangeRects[2] = SkRect::MakeXYWH(0.0f, currY,
- kCtrlRange + kSliderWidth,
- kSliderHeight);
- fTypeCtrlRect = SkRect::MakeXYWH(kTypeCtrlInitialPos * kCtrlRange, currY,
- kSliderWidth, kSliderHeight);
- fBevelType = (SkNormalSource::BevelType) SkScalarFloorToInt(kTypeCtrlInitialPos);
- currY += 2 * kSliderHeight;
-
- fSelectedCtrlRect = nullptr;
+ fControlPanelSelected = false;
fDirtyNormalSource = true;
fLabelTypeface = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
SkPaint paint;
if (fDirtyNormalSource) {
- fNormalSource = SkNormalSource::MakeBevel(fBevelType, fBevelWidth, fBevelHeight);
+ fNormalSource = SkNormalSource::MakeBevel((SkNormalSource::BevelType)fBevelType,
+ fBevelWidth, fBevelHeight);
fDirtyNormalSource = false;
}
void onDrawContent(SkCanvas *canvas) override {
canvas->save();
- canvas->resetMatrix(); // Force static controls and labels
-
- // Draw controls
-
- SkPaint ctrlRectPaint;
- ctrlRectPaint.setColor(0xFFF3F3F3);
- canvas->drawRect(fWidthCtrlRect, ctrlRectPaint);
- canvas->drawRect(fHeightCtrlRect, ctrlRectPaint);
- canvas->drawRect(fTypeCtrlRect, ctrlRectPaint);
-
- SkPaint ctrlRectRangePaint;
- ctrlRectRangePaint.setColor(0xFFFFFFFF);
- ctrlRectRangePaint.setStyle(SkPaint::kStroke_Style);
- ctrlRectRangePaint.setStrokeWidth(2.0f);
-
- for (size_t i = 0; i < kNumControls; i++) {
- canvas->drawRect(fCtrlRangeRects[i], ctrlRectRangePaint);
- }
-
- // Draw labels
- constexpr SkScalar kTextSize = 12.0f;
- SkString widthLabel, heightLabel, typeLabel;
- SkPaint labelPaint;
- labelPaint.setTypeface(fLabelTypeface);
- labelPaint.setAntiAlias(true);
- labelPaint.setColor(0xFFFFFFFF);
- labelPaint.setTextSize(kTextSize);
-
- widthLabel.appendf("BevelWidth: %f", fBevelWidth);
- heightLabel.appendf("BevelHeight: %f", fBevelHeight);
- typeLabel.append("BevelType: ");
+ canvas->resetMatrix(); // Force static control panel position
+ fControlPanel.drawContent(canvas);
+ canvas->restore();
- switch (fBevelType) {
- case SkNormalSource::BevelType::kLinear:
- typeLabel.append("Linear");
- break;
- case SkNormalSource::BevelType::kRoundedIn:
- typeLabel.append("RoundedIn");
- break;
- case SkNormalSource::BevelType::kRoundedOut:
- typeLabel.append("RoundedOut");
- break;
+ SkLights::Builder builder;
+ for (int i = 0; i < kNumLights; i++) {
+ builder.add(SkLights::Light::MakeDirectional(fLightDefs[i].fColor,
+ SkPoint3::Make(fLightDefs[i].fDirXY.fX,
+ fLightDefs[i].fDirXY.fY,
+ fLightDefs[i].fDirZ)));
}
-
- canvas->drawText(widthLabel.c_str(), widthLabel.size(), 0,
- fWidthCtrlRect.fTop - kTextSize/2.0f, labelPaint);
- canvas->drawText(heightLabel.c_str(), heightLabel.size(), 0,
- fHeightCtrlRect.fTop - kTextSize/2.0f, labelPaint);
- canvas->drawText(typeLabel.c_str(), typeLabel.size(), 0,
- fTypeCtrlRect.fTop - kTextSize/2.0f, labelPaint);
-
- canvas->restore(); // Return to modified matrix when drawing shapes
+ builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.4f, 0.4f, 0.4f)));
+ fLights = builder.finish();
// Draw shapes
SkScalar xPos = kCtrlRange + 25.0f;
}
bool onClick(Click *click) override {
- SkScalar x = click->fCurr.fX;
- SkScalar y = click->fCurr.fY;
-
- SkScalar dx = x - click->fPrev.fX;
- SkScalar dy = y - click->fPrev.fY;
-
- // Control deselection
- if (Click::State::kUp_State == click->fState) {
- fSelectedCtrlRect = nullptr;
- return true;
- }
-
- // Control selection
- if (nullptr == fSelectedCtrlRect && Click::State::kDown_State == click->fState) {
- if (fWidthCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
- fSelectedCtrlRect = &fWidthCtrlRect;
- } else if (fHeightCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
- fSelectedCtrlRect = &fHeightCtrlRect;
- } else if (fTypeCtrlRect.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
- fSelectedCtrlRect = &fTypeCtrlRect;
- }
- }
-
- if (nullptr != fSelectedCtrlRect) { // Control modification
- fSelectedCtrlRect->offsetTo(SkScalarPin(x, 0.0f, kCtrlRange), fSelectedCtrlRect->fTop);
-
- fBevelHeight = (fHeightCtrlRect.fLeft / kCtrlRange) * kBevelHeightMax * 2.0f
- - kBevelHeightMax;
- fBevelWidth = (fWidthCtrlRect.fLeft / kCtrlRange) * kBevelWidthMax;
- fBevelType = (SkNormalSource::BevelType)SkTMin(
- SkScalarFloorToInt(kBevelTypeCount * fTypeCtrlRect.fLeft / kCtrlRange),
- kBevelTypeCount - 1);
-
- // Snap type controls to 3 positions
- fTypeCtrlRect.offsetTo(kCtrlRange * ( ((int)fBevelType)/SkIntToScalar(kBevelTypeCount)
- + 1.0f/(2.0f * kBevelTypeCount) ),
- fTypeCtrlRect.fTop);
-
- // Ensuring width is non-zero
- fBevelWidth = SkMaxScalar(1.0f, fBevelWidth);
+ // Control panel mouse handling
+ fControlPanelSelected = fControlPanel.inClick(click);
+ if (fControlPanelSelected) { // Control modification
fDirtyNormalSource = true;
this->inval(nullptr);
return true;
- } else { // Moving light
- if (dx != 0 || dy != 0) {
- float recipX = 1.0f / kAppWidth;
- float recipY = 1.0f / kAppHeight;
-
- if (0 == click->fModifierKeys) { // No modifier
- fBlueLight = SkLights::Light::MakeDirectional(fBlueLight.color(),
- SkVector3::Make((kAppWidth/2.0f - x) * recipX * -3.0f,
- (kAppHeight/2.0f - y) * recipY * -3.0f,
- 1.0f));
- } else if (1 == click->fModifierKeys) { // Shift key
- fRedLight = SkLights::Light::MakeDirectional(fRedLight.color(),
- SkVector3::Make((kAppWidth/2.0f - x) * recipX * -3.0f,
- (kAppHeight/2.0f - y) * recipY * -3.0f,
- 1.0f));
- }
-
- SkLights::Builder builder;
- builder.add(fRedLight);
- builder.add(fBlueLight);
- builder.add(SkLights::Light::MakeAmbient(
- SkColor3f::Make(0.4f, 0.4f, 0.4f)));
- fLights = builder.finish();
-
- this->inval(nullptr);
- }
- return true;
}
+ // TODO move shapes
+ this->inval(nullptr);
return true;
}
private:
static constexpr int kNumTestRects = 3;
- static constexpr SkScalar kAppWidth = 400.0f;
- static constexpr SkScalar kAppHeight = 400.0f;
static constexpr SkScalar kShapeBoundsSize = 120.0f;
static constexpr SkScalar kCtrlRange = 150.0f;
- static constexpr SkScalar kBevelWidthMax = kShapeBoundsSize;
- static constexpr SkScalar kBevelHeightMax = 50.0f;
- static constexpr int kBevelTypeCount = 3;
- static constexpr SkScalar kSliderHeight = 20.0f;
- static constexpr SkScalar kSliderWidth = 10.0f;
+ static constexpr int kNumLights = 3;
const SkRect fShapeBounds;
- static constexpr int kNumControls = 3;
- SkRect fCtrlRangeRects[kNumControls];
- SkRect* fSelectedCtrlRect;
- SkRect fWidthCtrlRect;
- SkRect fHeightCtrlRect;
- SkRect fTypeCtrlRect;
-
SkScalar fBevelWidth;
SkScalar fBevelHeight;
- SkNormalSource::BevelType fBevelType;
+ int fBevelType;
+
sk_sp<SkNormalSource> fNormalSource;
bool fDirtyNormalSource;
sk_sp<SkLights> fLights;
- SkLights::Light fRedLight;
- SkLights::Light fBlueLight;
+
+ struct LightDef {
+ SkVector fDirXY;
+ SkScalar fDirZ;
+ SkColor3f fColor;
+
+ LightDef() {}
+ LightDef(SkVector dirXY, SkScalar dirZ, SkColor3f color)
+ : fDirXY(dirXY)
+ , fDirZ(dirZ)
+ , fColor(color) {}
+ };
+ LightDef fLightDefs[kNumLights];
+
+ ControlPanel fControlPanel;
+ bool fControlPanelSelected;
sk_sp<SkTypeface> fLabelTypeface;