fustyles / BlocklyResearch

5 stars 4 forks source link

更新物件導向的程式寫法 #2

Open ianfun opened 11 months ago

ianfun commented 11 months ago

Blockly似乎建議我們使用 class 而不是 function 的物件導向寫法

我看了 Blockly 的文件,Blockly.utils.object.inheritsgoogl.inherits 似乎都被移除了

fuFieldsImageDropdown 為例,程式應該長這樣

  class fuFieldsImageDropdown extends Blockly.FieldTextInput {
    constructor(text, options, opt_validate, opt_width, opt_height, opt_imageField) {
      super(text, opt_validate);
      this.textSize = 14;  //px
      this.imageSize = 24;  //px
      //If you change textSize value or imageSize value, you need to get divRowHeight value by testing.
      this.divRowHeight = (options[0].length>1)?30.9:24.5;  

      this.divWidth = opt_width||200;
      this.divHeight = opt_height||100;
      this.imageField = opt_imageField;

      this.divPadding = 5;
      this.originList = options;
      this.showList = [];

      this.setSpellcheck(false);
      this.clickWrapper_ = null;
      this.moveWrapper_ = null;
      this.downWrapper_ = null; 
    }
    fromJson() {
      return new fuFieldsImageDropdown(this.options['FieldsImageDropdown']);
    }
    showEditor_() {
      super.showEditor_(this);

      var div = Blockly.WidgetDiv.getDiv();
      if (!div.firstChild) {
        return;
      }
      var editor = this.dropdownCreate_();
      Blockly.DropDownDiv.getContentDiv().appendChild(editor);

      Blockly.DropDownDiv.setColour(this.sourceBlock_.style.colourPrimary,this.sourceBlock_.style.colourTertiary);

      Blockly.DropDownDiv.showPositionedByField(
        this, this.dropdownDispose_.bind(this));

      this.clickWrapper_ =
        Blockly.browserEvents.bind(this.imageElement_, 'click', this,
          this.hide_);
      this.moveWrapper_ =
        Blockly.browserEvents.bind(this.imageElement_, 'mousemove', this,
          this.onMouseMove);
      this.downWrapper_ =
        Blockly.browserEvents.bind(this.imageElement_, 'mousedown', this,
          this.onMouseDown);

      this.updateGraph_();
    }

class的寫法不僅易讀,也符合Javascript的趨勢

ianfun commented 11 months ago

另外,我想提一些建議

this.imageElement_.innerHTML = highLight.join("<br>"); 等,可能會造成使用上的延遲。更好的作法是直接修改那個 element 的 style 而不是直接替換 HTML

例如

    dropdownCreate_() {
      this.imageElement_ = document.createElement('div');
      this.imageElement_.style.padding = this.divPadding;
      this.imageElement_.style.height = this.divHeight;
      this.imageElement_ .style.width = this.divWidth;
      this.imageElement_.style.fontSize = this.textSize;
      this.imageElement_.style.whiteSpace = 'nowrap';
      this.showList = [];
      for (const X of this.originList) {
        const d = document.createElement('div');
        const x = new Image(this.imageSize, this.imageSize);
        x.style.verticalAlign = 'middle';
        x.src = X[1];
        const y = document.createElement('span');
        y.textContent = X[0];
        y.style.float = 'right';
        d.appendChild(x);
        d.appendChild(y);
        this.imageElement_.appendChild(d);
      }
      return this.imageElement_;
    }

image

ianfun commented 11 months ago

可以添加class讓它更像原本的樣式

    dropdownCreate_(div) {
      for (const X of this.originList) {
        const d = document.createElement('div');
        const x = new Image(this.imageSize, this.imageSize);
        x.style.verticalAlign = 'middle';
        x.src = X[1];
        const y = document.createElement('span');
        y.textContent = X[0];
        y.style.float = 'right';
        d.appendChild(x);
        d.appendChild(y);
        d.classList.add('blocklyDropdownMenu', 'blocklyMenuItem');
        d.onpointerover = function() {
          d.classList.add('blocklyMenuItemHighlight');
        }
        d.onpointerleave = function() {
          d.classList.remove('blocklyMenuItemHighlight');
        }
        div.appendChild(d);
      }
    }

image

onclick, onmouseover 等其實也可以直接綁定在 child 裡,這樣就不用計算高度等

ianfun commented 11 months ago

image

ianfun commented 11 months ago

我不知道為什麼不能連續選擇

以下是測試用版本

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Blockly Demo</title>

<script src="https://unpkg.com/blockly/blockly_compressed.js"></script>
<script src="https://unpkg.com/blockly/blocks_compressed.js"></script>
<script src="https://unpkg.com/blockly/msg/zh-hant.js"></script>

</head>
<body>
<style>
body, html {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
}
.blocklyMenuItemSelected .blocklyMenuItemCheckbox {
  position: absolute !important;
}
</style>

<div id="blocklyDiv" style="height: 100%; width: 100%;"></div>

<script>
var toolbox = {
  "kind": "categoryToolbox",
  "contents": [
    {
      "kind": "category",
      "name": "TEST",
      "categorystyle": "logic_category",
      "contents": [{
          "kind": "block",
          "type": "TEST1"
        },
        {
          "kind": "block",
          "type": "math_constant"
        }
      ]
    }
  ]
}
window.onload =  function() {
  class ImageTextDropDown extends Blockly.FieldTextInput {
    constructor(text, options) {
      super(Blockly.Field.SKIP_SETUP);
      this.opt = options;
      this.setSpellcheck(false);
    }
    fromJson() {
      return new ImageTextDropDown(this.options['FieldsImageDropdown']);
    }
    showEditor_(e) {
      super.showEditor_(this, e, true);
      this.htmlInput_.disabled = true;
      var div = Blockly.WidgetDiv.getDiv();
      if (!div.firstChild) {
        return;
      }
      this.dropdownCreate_(Blockly.DropDownDiv.getContentDiv());
      Blockly.DropDownDiv.showPositionedByField(this);
    }
    dropdownCreate_(div) {
      const cur = this.getValue() || 0;
      div.style.paddingLeft = '30px';
      const self = this;
      let i = 0;
      for (const X of this.opt) {
        const d = document.createElement('div');
        const D = document.createElement('div');
        const x = new Image(24, 24);
        x.src = X[1];
        const y = document.createElement('span');
        y.style.marginLeft = '1em';
        y.textContent = X[0];
        y.style.float = 'right';
        const check = document.createElement('div');
        check.classList.add('blocklyMenuItemCheckbox', 'goog-menuitem-checkbox');
        D.appendChild(check);
        D.style.display = 'flex';
        D.style.alignItems = 'center';
        D.appendChild(x);
        D.appendChild(y);
        D.classList.add('blocklyMenuItemContent', 'goog-menuitem-content');
        d.classList.add('blocklyDropdownMenu', 'blocklyMenuItem');
        if (i == cur)
          d.classList.add('blocklyMenuItemSelected');
        // https://github.com/google/blockly/blob/dc61e487b4e0024a53866e8aff652c0b108a0340/core/menu.ts#L102
        d.onpointerenter = function(event) {
          event.currentTarget.focus();
        }
        d.onpointerover = function() {
          d.classList.add('blocklyMenuItemHighlight');
        }
        d.onpointerleave = function() {
          d.classList.remove('blocklyMenuItemHighlight');
        }
        d.onpointerup = function(event) {
          self.setEditorValue_(event.currentTarget.i);
          Blockly.DropDownDiv.hide();
        }
        d.appendChild(D);
        div.appendChild(d);
        d.i = i;
        ++i;
      }
    }
    valueToText(value) {
      return this.opt[Number(value)][0];
    }
    textToValue(text) {
      for (let i = 0;i < this.opt.length;++i) {
        if (this.opt[i][0] == text)
          return i;
      }
      return -1;
    }
    getText_() {
      if (this.isBeingEdited_)
        return super.getText_();
      return this.opt[this.getValue() || 0][0];
    }

    getEditorText_(value) {
      return this.valueToText(value);
    }

    getValueFromEditorText_(text) {
      return this.textToValue(text);
    }

    doClassValidation_(opt_newValue) {
      return;
    }
  }

  Blockly.Blocks["TEST1"] = {
    init: function() {

      var options = [
        ['CLOUDY', "https://imgur.com/Hi33BEx.png", "ccc"],
        ['PARTLY-CLOUDY', "https://imgur.com/rX0np7I.png", "ppp"],
        ['MOON', "https://imgur.com/ulJIWW4.png", "mmm"],
        ['RAIN', "https://imgur.com/wRwu4pZ.png", "rrr"],
        ['STAR', "https://imgur.com/KMWOcGf.png", "sss"]
      ];
      var dropdownWidth = 200;
      var dropdownHeight = 100;

      var imageField = new Blockly.FieldImage(options[0][1], 18, 18, {
        alt: "*",
        flipRtl: "FALSE"
      });
      var field = new ImageTextDropDown('', options, this.validate, dropdownWidth, dropdownHeight, imageField);

      this.appendDummyInput()
        .appendField(imageField, "i");
      this.appendDummyInput()
        .appendField(field, 'd');

      this.setInputsInline(true);
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(100);
    }
  };
  const workspace = Blockly.inject('blocklyDiv', {
    toolbox: toolbox
  });
};
</script>

</body>
</html>
fustyles commented 11 months ago

謝謝您提供了寶貴的建議,我測試了您改寫的程式碼並比對了我原本的程式碼,這問題有可能是您省略了少部分看似不必要的函式造成,我會再另找時間測試。