| Index: samplecode/SampleBevel.cpp
|
| diff --git a/samplecode/SampleBevel.cpp b/samplecode/SampleBevel.cpp
|
| index 868ce35244288fa99b9c76c4ff0c75705650c173..e592cf191aa38e11e9cf0f0aa559bb8685475a96 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;
|
|
|
|
|