diff options
-rw-r--r-- | samplecode/SampleBevel.cpp | 881 |
1 files changed, 704 insertions, 177 deletions
diff --git a/samplecode/SampleBevel.cpp b/samplecode/SampleBevel.cpp index 868ce35244..e592cf191a 100644 --- a/samplecode/SampleBevel.cpp +++ b/samplecode/SampleBevel.cpp @@ -11,57 +11,682 @@ #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()); @@ -87,7 +712,8 @@ protected: SkPaint paint; if (fDirtyNormalSource) { - fNormalSource = SkNormalSource::MakeBevel(fBevelType, fBevelWidth, fBevelHeight); + fNormalSource = SkNormalSource::MakeBevel((SkNormalSource::BevelType)fBevelType, + fBevelWidth, fBevelHeight); fDirtyNormalSource = false; } @@ -112,58 +738,19 @@ protected: 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; @@ -183,116 +770,56 @@ protected: } 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; |