Skip to content

Commit

Permalink
feat: shape (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
devlzl authored Jan 15, 2024
1 parent 38db6dd commit d96a128
Show file tree
Hide file tree
Showing 15 changed files with 237 additions and 28 deletions.
2 changes: 2 additions & 0 deletions src/BlockHub/BlockHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TextBox from './TextBoxBlock/TextBox.vue'
import Table from './TableBlock/Table.vue'
import Picture from './PictureBlock/Picture.vue'
import Canvas from './CanvasBlock/Canvas.vue'
import Shape from './ShapeBlock/Shape.vue'

class BlockHub {
private _blockMap = Object.create(null)
Expand All @@ -23,6 +24,7 @@ export const BlockViews: { [key: string]: Component } = {
Table,
Picture,
Canvas,
Shape,
}

export const blockHub = new BlockHub()
44 changes: 44 additions & 0 deletions src/BlockHub/ShapeBlock/Shape.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ShapeBlock } from './ShapeBlock'
import { BasicPropName } from '@BlockHub/Block/Block'
import { Square, Triangle, Round, Star, HexagonOne } from '@icon-park/vue-next'
const { block } = defineProps<{
block: ShapeBlock
}>()
const { x, y, width, height, rotate, shape } = block
const props = ref({ x, y, width, height, rotate })
block.props.events.update.on(({ key, to }) => {
if (['x', 'y', 'width', 'height', 'rotate'].includes(key)) {
const name = key as BasicPropName
props.value[name] = to as number
}
})
const ShapeComponent = {
rect: Square,
triangle: Triangle,
circle: Round,
star: Star,
hexagon: HexagonOne,
}[shape]
</script>

<template>
<component
class="absolute"
:is="ShapeComponent"
:size="props.width"
theme="filled"
fill="#4f71be"
:style="{
left: `${props.x}px`,
top: `${props.y}px`,
width: `${props.width}px`,
height: `${props.height}px`,
rotate: `${props.rotate}deg`,
}"
></component>
</template>
32 changes: 32 additions & 0 deletions src/BlockHub/ShapeBlock/ShapeBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Block } from '../Block/Block'

export type Shape = 'rect' | 'triangle' | 'circle' | 'star' | 'hexagon'

export class ShapeBlock extends Block {
constructor(shape: string, x: number, y: number) {
super('Shape', x, y, 0, 0)
this.props.set('shape', shape)
}

get width() {
return this.props.get('width') as number
}

set width(value: number) {
this.props.set('width', value)
this.props.set('height', value)
}

get height() {
return this.props.get('height') as number
}

set height(value: number) {
this.props.set('height', value)
this.props.set('width', value)
}

get shape() {
return this.props.get('shape') as Shape
}
}
3 changes: 3 additions & 0 deletions src/Const/block.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export const TEXT_BOX_DEFAULT_WIDTH = 300
export const TEXT_BOX_DEFAULT_HEIGHT = 100

export const SHAPE_DEFAULT_WIDTH = 200
export const SHAPE_DEFAULT_HEIGHT = 200
24 changes: 19 additions & 5 deletions src/Kernel/ToolBox/ToolBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { EventManager } from '@Kernel/EventManager'
import { controllers } from './controller/_index'

export type ToolType = 'Default' | 'TextBox' | 'Shape' | 'Picture' | 'Table' | 'Pen'
export type ToolConfig = {
[key: string]: string | number | boolean | ToolConfig
}

export class ToolBox {
private _currentToolType: ToolType = 'Default'
private _currentTool: { type: ToolType; config?: ToolConfig } = { type: 'Default' }
private _slideElement?: HTMLElement

events = {
Expand All @@ -17,7 +20,7 @@ export class ToolBox {
}

private get _currentController() {
return controllers[this._currentToolType]
return controllers[this._currentTool.type]
}

private _bindEvents() {
Expand Down Expand Up @@ -53,14 +56,25 @@ export class ToolBox {
}

_addListener()
this.events.toolChange.on((toolType) => {
this._currentToolType = toolType
this.events.toolChange.on(() => {
_removeListener()
_addListener()
})
}

get currentToolType() {
return this._currentToolType
return this._currentTool.type
}

get currentTool() {
return this._currentTool
}

changeTool(toolType: ToolType, config: ToolConfig = {}) {
this._currentTool = {
type: toolType,
config: config,
}
this.events.toolChange.emit(toolType)
}
}
2 changes: 1 addition & 1 deletion src/Kernel/ToolBox/controller/PenToolController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ export class PenToolController extends ToolController {
slide.addBlock(block)
selectionManager.focus(block)
}
toolBox.events.toolChange.emit('Default')
toolBox.changeTool('Default')
}
}
58 changes: 58 additions & 0 deletions src/Kernel/ToolBox/controller/ShapeToolController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Shape, ShapeBlock } from '@BlockHub/ShapeBlock/ShapeBlock'
import { toSlideCoords } from '@Utils/toSlideCoords'
import { selectionManager, slideManager, toolBox } from '@Kernel/index'
import { SHAPE_DEFAULT_HEIGHT, SHAPE_DEFAULT_WIDTH } from '@Const/block'
import { ToolController } from './_ToolController'
import { ToolConfig } from '../ToolBox'

export class ShapeToolController extends ToolController {
private _dragging = false
private _currentBlock?: ShapeBlock
private _startX = 0
private _startY = 0

handleClick(event: MouseEvent) {}

handleMouseDown(event: MouseEvent) {
const { x, y } = toSlideCoords(event.currentTarget as HTMLElement, event.clientX, event.clientY)
const shape = (toolBox.currentTool.config as ToolConfig).shape as Shape
this._startX = x
this._startY = y
this._currentBlock = new ShapeBlock(shape, x, y)
slideManager.currentSlide.addBlock(this._currentBlock)
}

handleMouseMove(event: MouseEvent): void {
const { x, y } = toSlideCoords(event.currentTarget as HTMLElement, event.clientX, event.clientY)
const block = this._currentBlock
if (block) {
this._dragging = true
const left = Math.min(this._startX, x)
const top = Math.min(this._startY, y)
const right = Math.max(this._startX, x)
const bottom = Math.max(this._startY, y)
const width = right - left
const height = bottom - top
const edge = Math.min(width, height)
block.x = left
block.y = top
block.width = edge
block.height = edge
}
}

handleMouseUp(event: MouseEvent): void {
const block = this._currentBlock
if (!block) {
return
}
if (!this._dragging) {
block.width = SHAPE_DEFAULT_WIDTH
block.height = SHAPE_DEFAULT_HEIGHT
}
this._currentBlock = undefined
this._dragging = false
selectionManager.focus(block)
toolBox.changeTool('Default')
}
}
2 changes: 1 addition & 1 deletion src/Kernel/ToolBox/controller/TextBoxToolController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ export class TextBoxToolController extends ToolController {
this._currentBlock = undefined
this._dragging = false
selectionManager.focus(block)
toolBox.events.toolChange.emit('Default')
toolBox.changeTool('Default')
}
}
2 changes: 2 additions & 0 deletions src/Kernel/ToolBox/controller/_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { PenToolController } from './PenToolController'
import { PictureToolController } from './PictureToolController'
import { TextBoxToolController } from './TextBoxToolController'
import { ToolController } from './_ToolController'
import { ShapeToolController } from './ShapeToolController'

export const controllers: { [key: string]: ToolController } = {
Default: new DefaultToolController(),
Picture: new PictureToolController(),
TextBox: new TextBoxToolController(),
Pen: new PenToolController(),
Shape: new ShapeToolController(),
}
11 changes: 10 additions & 1 deletion src/UserInterface/Slide/Slide.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,22 @@ const selectedBlocks = shallowRef(selectionManager.selectedBlocks)
selectionManager.events.update.on(() => {
selectedBlocks.value = selectionManager.selectedBlocks
})
const toolType = ref(toolBox.currentToolType)
toolBox.events.toolChange.on(() => {
toolType.value = toolBox.currentToolType
})
</script>

<template>
<div
ref="slideRef"
class="relative bg-white shadow-lg"
:style="{ width: `${DEFAULT_SLIDE_WIDTH}px`, height: `${DEFAULT_SLIDE_HEIGHT}px` }"
:style="{
width: `${DEFAULT_SLIDE_WIDTH}px`,
height: `${DEFAULT_SLIDE_HEIGHT}px`,
cursor: toolType === 'TextBox' || toolType === 'Shape' ? 'crosshair' : 'default',
}"
>
<SelectedBox />
<component
Expand Down
2 changes: 1 addition & 1 deletion src/UserInterface/ToolBar/components/Draw/Tools.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { toolBox } from '@Kernel/index'

<template>
<ButtonGroup :name="$t('ToolBar.draw.tools.title')">
<button class="menu-btn flex flex-col items-center" @click="toolBox.events.toolChange.emit('Pen')">
<button class="menu-btn flex flex-col items-center" @click="toolBox.changeTool('Pen')">
<Pencil theme="outline" size="32" fill="#333" :strokeWidth="2" />
<span class="text-xs mt-1">{{ $t('ToolBar.draw.tools.pen') }}</span>
</button>
Expand Down
12 changes: 3 additions & 9 deletions src/UserInterface/ToolBar/components/Home/Draw.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
<script setup lang="ts">
import { Stickers, BackgroundColor, Copy, Platte, GraphicDesign, BringToFrontOne } from '@icon-park/vue-next'
import { Stickers, BackgroundColor, Copy, Platte, BringToFrontOne } from '@icon-park/vue-next'
import ButtonGroup from '../ButtonGroup.vue'
import ToolButton from '../ToolButton.vue'
import Shapes from '../common/Shapes.vue'
</script>

<template>
<ButtonGroup :name="$t('ToolBar.home.drawing.title')">
<div class="flex">
<ToolButton :hasMenu="true">
<template #icon>
<GraphicDesign theme="two-tone" size="32" :fill="['#333', '#83BEEC']" :strokeWidth="1" />
</template>
<template #name>{{ $t('ToolBar.home.drawing.shapes') }}</template>
<template #menu>draw menu test</template>
</ToolButton>
<Shapes />

<!-- <button class="menu-btn flex flex-col items-center justify-center">
<bring-to-front-one theme="two-tone" size="32" :fill="['#333', '#F8DB8F']" :strokeWidth="1" />
Expand Down
12 changes: 3 additions & 9 deletions src/UserInterface/ToolBar/components/Insert/Illustration.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
<script setup lang="ts">
import { GraphicDesign, PictureAlbum, Down, Outdoor, GameEmoji } from '@icon-park/vue-next'
import { PictureAlbum, Down, Outdoor, GameEmoji } from '@icon-park/vue-next'
import ButtonGroup from '../ButtonGroup.vue'
import ToolButton from '../ToolButton.vue'
import Shapes from '../common/Shapes.vue'
</script>

<template>
<ButtonGroup :name="$t('ToolBar.insert.illustration.title')">
<div class="flex">
<ToolButton :clickHandler="() => {}">
<template #icon>
<GraphicDesign theme="two-tone" size="32" :fill="['#333', '#83BEEC']" :strokeWidth="1" />
</template>
<template #name>{{ $t('ToolBar.insert.illustration.shapes') }}</template>
</ToolButton>

<Shapes />
<!-- <button class="flex flex-col items-center justify-center py-0 -mt-4 menu-btn">
<PictureAlbum theme="multi-color" size="32" :fill="['#333', '#83beec', '#FFF', '#43CCF8']" :strokeWidth="2" />
<span class="mt-1 text-xs">{{ $t('ToolBar.insert.illustration.stockImages') }}</span>
Expand Down
2 changes: 1 addition & 1 deletion src/UserInterface/ToolBar/components/Insert/Text.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ToolButton from '../ToolButton.vue'

<template>
<ButtonGroup :name="$t('ToolBar.insert.textBox.title')">
<ToolButton :clickHandler="() => toolBox.events.toolChange.emit('TextBox')">
<ToolButton :clickHandler="() => toolBox.changeTool('TextBox')">
<template #icon>
<TextRecognition
class="mb-1"
Expand Down
57 changes: 57 additions & 0 deletions src/UserInterface/ToolBar/components/common/Shapes.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script setup lang="ts">
import { toolBox } from '@Kernel/index'
import ToolButton from '../ToolButton.vue'
import { GraphicDesign, Square, Triangle, Round, Star, HexagonOne } from '@icon-park/vue-next'
</script>
<template>
<ToolButton :hasMenu="true">
<template #icon>
<GraphicDesign theme="two-tone" size="32" :fill="['#333', '#83BEEC']" :strokeWidth="1" />
</template>
<template #name>{{ $t('ToolBar.insert.illustration.shapes') }}</template>
<template #menu>
<div class="p-[10px] flex flex-wrap items-center gap-[10px] w-[172px]">
<Square
class="menu-btn cursor-pointer"
theme="filled"
size="36"
fill="#4d4d4d"
:strokeWidth="2"
@click="toolBox.changeTool('Shape', { shape: 'rect' })"
/>
<Triangle
class="menu-btn cursor-pointer"
theme="filled"
size="36"
fill="#4d4d4d"
:strokeWidth="2"
@click="toolBox.changeTool('Shape', { shape: 'triangle' })"
/>
<Round
class="menu-btn cursor-pointer"
theme="filled"
size="36"
fill="#4d4d4d"
:strokeWidth="2"
@click="toolBox.changeTool('Shape', { shape: 'circle' })"
/>
<Star
class="menu-btn cursor-pointer"
theme="filled"
size="36"
fill="#4d4d4d"
:strokeWidth="2"
@click="toolBox.changeTool('Shape', { shape: 'star' })"
/>
<HexagonOne
class="menu-btn cursor-pointer"
theme="filled"
size="36"
fill="#4d4d4d"
:strokeWidth="2"
@click="toolBox.changeTool('Shape', { shape: 'hexagon' })"
/>
</div>
</template>
</ToolButton>
</template>

0 comments on commit d96a128

Please sign in to comment.