Skip to content

Open mutator dialog will cause part of the toolbox not visible #3366

@KAINIMAL

Description

@KAINIMAL

Expected Behavior

When open a mutator dialog, toolbox remains visible.

Actual Behavior

When open the if else mutator dialog, part of the toolbox not visible like bellow:
Image

when close the mutator dialog, tool box back to normal

Image

Steps to Reproduce

Steps to reproduce the behavior:

  1. create a if else block
import * as Blockly from 'scratch-blocks'

import { BlockInterface } from '@/components/ir-program/program-blockly/blockly-lib/blocks/interface'
import { i18n } from '@/i18n'

/**
 * 代表逻辑 if-else 的 Block
 */
const logicalIfElseBlock: BlockInterface = {
  init() {
    this.jsonInit({
      type: 'logical_if',
      message0: i18n.tc('blockly.block.ifDo'),
      args0: [
        {
          type: 'input_value',
          name: 'IF0',
          check: 'Boolean'
        }
      ],
      message1: '%1',
      args1: [
        {
          type: 'input_statement',
          name: 'DO0'
        }
      ],
      mutator: 'controls_if_mutator',
      category: Blockly.Categories.control,
      extensions: ['colours_control', 'shape_statement']
    })
  },
  gbtInfo() {
    const info = {
      tagName: 'logical',
      type: 'if'
    }
    return info
  }
}

/**
 * 代表 logical_if 变量中 else-if 语句的代码块。
 */
const mutatorElseIfBlock: BlockInterface = {
  init() {
    this.jsonInit({
      type: 'mutator_if_elseif',
      message0: i18n.tc('blockly.block.elseIf'),
      category: Blockly.Categories.control,
      extensions: ['colours_control', 'shape_statement']
    })
  },
  gbtInfo() {
    const info = {
      tagName: 'vice_logical',
      type: 'elif'
    }
    return info
  }
}

/**
 * 代表 logical_if 变量中 else 语句的代码块。
 */
const mutatorElseBlock: BlockInterface = {
  init() {
    this.jsonInit({
      type: 'mutator_if_else',
      message0: i18n.tc('blockly.block.else'),
      category: Blockly.Categories.control,
      extensions: ['colours_control', 'shape_end']
    })
  },
  gbtInfo() {
    const info = {
      tagName: 'logical',
      type: 'else'
    }
    return info
  }
}

/**
 * 代表 logical_if 变量中 if 语句的代码块。
 */
const mutatorIfContainerBlock: BlockInterface = {
  init() {
    this.jsonInit({
      type: 'mutator_if_if',
      message0: i18n.tc('blockly.block.if'),
      nextStatement: false,
      category: Blockly.Categories.control,
      extensions: ['colours_control', 'shape_hat']
    })
  },
  gbtInfo() {
    const info = {
      tagName: 'logical_title',
      type: 'if_title'
    }
    return info
  }
}

/**
 * Mutator 方法
 */
const CONTROLS_IF_MUTATOR_MIXIN = {
  elseifCount_: 0,
  elseCount_: 0,

  /**
   * 创建 XML 来表示 else-if 和 else 输入的数量。
   */
  mutationToDom(this): Element | null {
    if (!this.elseifCount_ && !this.elseCount_) {
      return null
    }
    const container = document.createElement('mutation')
    if (this.elseifCount_) {
      container.setAttribute('elseif', String(this.elseifCount_))
    }
    if (this.elseCount_) {
      container.setAttribute('else', '1')
    }
    return container
  },
  /**
   * 解析 XML 以恢复 else-if 和 else 输入。
   */
  domToMutation(this, xmlElement: Element) {
    this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif') ?? '0', 10)
    this.elseCount_ = parseInt(xmlElement.getAttribute('else') ?? '0', 10)
    this.updateShape_()
  },
  /**
   * 用该程序块的组件填充变形器对话框。
   *
   * @param workspace MutatorIcon 的工作区。
   * @returns Root block in mutator.
   */
  decompose(this, workspace: any) {
    const containerBlock = workspace.newBlock('mutator_if_if')
    containerBlock.initSvg()
    let connection = containerBlock.nextConnection
    for (let i = 1; i <= this.elseifCount_; i++) {
      const elseifBlock = workspace.newBlock('mutator_if_elseif')
      elseifBlock.initSvg()
      connection.connect(elseifBlock.previousConnection)
      connection = elseifBlock.nextConnection
    }
    if (this.elseCount_) {
      const elseBlock = workspace.newBlock('mutator_if_else')
      elseBlock.initSvg()
      connection.connect(elseBlock.previousConnection)
    }
    return containerBlock
  },
  /**
   * 根据变形器对话框的组件重新配置。
   *
   * @param containerBlock Root block in mutator.
   */
  compose(this, containerBlock: any) {
    let clauseBlock = containerBlock.nextConnection?.targetBlock()
    // 计算组件数量.
    this.elseifCount_ = 0
    this.elseCount_ = 0
    while (clauseBlock) {
      // 判断是否为插入标记区块
      if (clauseBlock.isInsertionMarker()) {
        clauseBlock = clauseBlock.getNextBlock()
        continue
      }
      switch (clauseBlock.type) {
        case 'mutator_if_elseif':
          this.elseifCount_++
          break
        case 'mutator_if_else':
          this.elseCount_++
          break
        default:
          throw TypeError(`Unknown block type: ${clauseBlock.type}`)
      }
      clauseBlock = clauseBlock.getNextBlock()
    }
    this.updateShape_()
  },
  /**
   * 存储指向任何相连子块的指针。
   *
   * @param containerBlock Root block in mutator.
   */
  saveConnections(this: any, containerBlock: any) {
    let clauseBlock = containerBlock.nextConnection?.targetBlock()
    let i = 1
    while (clauseBlock) {
      if (clauseBlock.isInsertionMarker()) {
        clauseBlock = clauseBlock.getNextBlock()
        continue
      }
      switch (clauseBlock.type) {
        case 'mutator_if_elseif': {
          const inputIf = this.getInput(`IF${i}`)
          const inputDo = this.getInput(`DO${i}`)
          clauseBlock.valueConnection_ = inputIf && inputIf.connection.targetConnection
          clauseBlock.statementConnection_ = inputDo && inputDo.connection.targetConnection
          i++
          break
        }
        case 'mutator_if_else': {
          const inputDo = this.getInput('ELSE')
          clauseBlock.statementConnection_ = inputDo && inputDo.connection.targetConnection
          break
        }
        default:
          throw TypeError(`Unknown block type: ${clauseBlock.type}`)
      }
      clauseBlock = clauseBlock.getNextBlock()
    }
  },
  /**
   * 更新程序块,以获得正确的输入数。
   */
  updateShape_(this: any) {
    // Delete everything.
    while (this.getInput('DummyDo')) {
      this.removeInput('DummyDo')
    }
    if (this.getInput('ELSE')) {
      this.removeInput('ELSE')
      this.removeInput('DummyElse')
    }
    for (let i = 1; this.getInput(`IF${i}`); i++) {
      this.removeInput(`IF${i}`)
      this.removeInput(`DO${i}`)
    }
    // 重塑
    for (let i = 1; i <= this.elseifCount_; i++) {
      this.appendValueInput(`IF${i}`).setCheck('Boolean').appendField(i18n.tc('blockly.block.elseIf'))
      this.appendDummyInput('DummyDo').appendField(i18n.tc('blockly.block.do'))
      this.appendStatementInput(`DO${i}`)
    }
    if (this.elseCount_) {
      this.appendDummyInput('DummyElse').appendField(i18n.tc('blockly.block.else'))
      this.appendStatementInput('ELSE')
    }
  }
}

/**
 * 注册变形器
 */
Blockly.Extensions.registerMutator('controls_if_mutator', CONTROLS_IF_MUTATOR_MIXIN, null, [
  'mutator_if_elseif',
  'mutator_if_else'
])

/**
 * 注册 logical_if blocks
 */
const registerBlocks = () => {
  Blockly.Blocks.logical_if = logicalIfElseBlock
  Blockly.Blocks.mutator_if_else = mutatorElseBlock
  Blockly.Blocks.mutator_if_elseif = mutatorElseIfBlock
  Blockly.Blocks.mutator_if_if = mutatorIfContainerBlock
}

export default registerBlocks
  1. Click on 'mutator icon'
  2. Check the toolbox

System Details

Windows 10 Chrome 126.0.6478.115

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions