feat: added basic tkinter widgets
This commit is contained in:
@@ -60,6 +60,11 @@ The discount's will be available for limited time only on pre-orders.
|
||||
| **Price** | - | ~~$129~~ $29 (save 77.52% for limited time on pre-order) | ~~180~~ $49 (Save 72.78% for a limited time on pre-orders) |
|
||||
| Pre-order now! | | [Get license]() | [Get license]() |
|
||||
|
||||
## Newsletter
|
||||
|
||||
Join the free newsletter to know about upcoming updates, learn how I built this tool and more about open-source.
|
||||
|
||||
[Join free newsletter](https://paulfreeman.substack.com/subscribe?utm_source=Github-Pybuilder)
|
||||
|
||||
## FAQ
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react"
|
||||
import { NotImplementedError } from "../../utils/errors"
|
||||
|
||||
import Tools from "../constants/tools"
|
||||
import { Layouts, PosType} from "../constants/layouts"
|
||||
import { Layouts, PosType } from "../constants/layouts"
|
||||
import Cursor from "../constants/cursor"
|
||||
import { toSnakeCase } from "../utils/utils"
|
||||
import EditableDiv from "../../components/editableDiv"
|
||||
@@ -40,8 +40,8 @@ class Widget extends React.Component {
|
||||
this._disableResize = false
|
||||
this._disableSelection = false
|
||||
|
||||
this.minSize = { width: 50, height: 50 } // disable resizing below this number
|
||||
this.maxSize = { width: 500, height: 500 } // disable resizing above this number
|
||||
this.minSize = { width: 10, height: 10 } // disable resizing below this number
|
||||
this.maxSize = { width: 2000, height: 2000 } // disable resizing above this number
|
||||
|
||||
this.cursor = Cursor.POINTER
|
||||
|
||||
@@ -68,14 +68,14 @@ class Widget extends React.Component {
|
||||
selected: false,
|
||||
widgetName: widgetName || 'widget', // this will later be converted to variable name
|
||||
enableRename: false, // will open the widgets editable div for renaming
|
||||
|
||||
|
||||
isDragging: false, // tells if the widget is currently being dragged
|
||||
dragEnabled: true,
|
||||
|
||||
widgetContainer: WidgetContainer.CANVAS, // what is the parent of the widget
|
||||
|
||||
showDroppableStyle: { // shows the droppable indicator
|
||||
allow: false,
|
||||
allow: false,
|
||||
show: false,
|
||||
},
|
||||
|
||||
@@ -103,11 +103,7 @@ class Widget extends React.Component {
|
||||
this.setAttrValue("styling.backgroundColor", value)
|
||||
}
|
||||
},
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER,
|
||||
value: "#000",
|
||||
},
|
||||
|
||||
label: "Styling"
|
||||
},
|
||||
layout: {
|
||||
@@ -153,6 +149,8 @@ class Widget extends React.Component {
|
||||
this.getWidgetType = this.getWidgetType.bind(this)
|
||||
this.getBoundingRect = this.getBoundingRect.bind(this)
|
||||
|
||||
this.getAttrValue = this.getAttrValue.bind(this)
|
||||
|
||||
this.getToolbarAttrs = this.getToolbarAttrs.bind(this)
|
||||
|
||||
// this.openRenaming = this.openRenaming.bind(this)
|
||||
@@ -173,7 +171,7 @@ class Widget extends React.Component {
|
||||
console.log("prior layout: ", this.state.attrs.layout.value)
|
||||
this.setLayout(this.state.attrs.layout.value)
|
||||
this.setWidgetStyling('backgroundColor', this.state.attrs.styling?.backgroundColor.value || "#fff")
|
||||
|
||||
|
||||
this.load(this.props.initialData || {}) // load the initial data
|
||||
|
||||
|
||||
@@ -210,37 +208,37 @@ class Widget extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
getToolbarAttrs() {
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Widget name", maxLength: 40},
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "width", max: this.maxSize.width, min: this.minSize.width},
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {placeholder: "height", max: this.maxSize.height, min: this.minSize.height},
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
@@ -266,7 +264,7 @@ class Widget extends React.Component {
|
||||
return this.state.attrs
|
||||
}
|
||||
|
||||
getId(){
|
||||
getId() {
|
||||
return this.__id
|
||||
}
|
||||
|
||||
@@ -287,9 +285,9 @@ class Widget extends React.Component {
|
||||
return this.state.selected
|
||||
}
|
||||
|
||||
setPosType(positionType){
|
||||
setPosType(positionType) {
|
||||
|
||||
if (!Object.values(PosType).includes(positionType)){
|
||||
if (!Object.values(PosType).includes(positionType)) {
|
||||
throw Error(`The Position type can only be among: ${Object.values(PosType).join(", ")}`)
|
||||
}
|
||||
|
||||
@@ -366,15 +364,37 @@ class Widget extends React.Component {
|
||||
// Traverse the state and update the nested value immutably
|
||||
let newAttrs = { ...this.state.attrs }
|
||||
let nestedObject = newAttrs
|
||||
|
||||
|
||||
keys.forEach(key => {
|
||||
nestedObject[key] = { ...nestedObject[key] } // Ensure immutability
|
||||
nestedObject = nestedObject[key]
|
||||
})
|
||||
|
||||
|
||||
nestedObject[lastKey].value = value
|
||||
|
||||
this.updateState({attrs: newAttrs})
|
||||
this.updateState({ attrs: newAttrs })
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the key as a path, retrieves the value for the widget attribute
|
||||
* @param {string} path - path to the key, eg: styling.backgroundColor
|
||||
* @returns {any} - the value at the given path
|
||||
*/
|
||||
getAttrValue(path) {
|
||||
const keys = path.split('.')
|
||||
|
||||
// Traverse the state and get the nested value
|
||||
let nestedObject = this.state.attrs
|
||||
|
||||
for (const key of keys) {
|
||||
if (nestedObject[key] !== undefined) {
|
||||
nestedObject = nestedObject[key]
|
||||
} else {
|
||||
return undefined // Return undefined if the key doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
return nestedObject?.value // Return the value (assuming it has a 'value' field)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,7 +406,7 @@ class Widget extends React.Component {
|
||||
|
||||
const serializeValues = (obj, currentPath = "") => {
|
||||
const result = {}
|
||||
|
||||
|
||||
for (let key in obj) {
|
||||
|
||||
if (ATTRS_KEYS.includes(key)) continue // don't serialize these as separate keys
|
||||
@@ -395,7 +415,7 @@ class Widget extends React.Component {
|
||||
// If the key contains a value property
|
||||
if (obj[key].hasOwnProperty('value')) {
|
||||
const path = currentPath ? `${currentPath}.${key}` : key;
|
||||
|
||||
|
||||
// If the value is an object, retain the entire value object
|
||||
if (typeof obj[key].value === 'object' && obj[key].value !== null) {
|
||||
result[path] = obj[key].value
|
||||
@@ -407,7 +427,7 @@ class Widget extends React.Component {
|
||||
Object.assign(result, serializeValues(obj[key], currentPath ? `${currentPath}.${key}` : key))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -427,9 +447,9 @@ class Widget extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
setLayout(value){
|
||||
setLayout(value) {
|
||||
// FIXME: when the parent layout is place, the child widgets should have position absolute
|
||||
const {layout, direction, grid={rows: 1, cols: 1}, gap=10} = value
|
||||
const { layout, direction, grid = { rows: 1, cols: 1 }, gap = 10 } = value
|
||||
|
||||
const widgetStyle = {
|
||||
...this.state.widgetStyling,
|
||||
@@ -481,7 +501,7 @@ class Widget extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
setResize(pos, size){
|
||||
setResize(pos, size) {
|
||||
// useful when resizing the widget relative to the canvas, sets all pos, and size
|
||||
this.updateState({
|
||||
size: size,
|
||||
@@ -542,7 +562,7 @@ class Widget extends React.Component {
|
||||
|
||||
if (Object.keys(data).length === 0) return // no data to load
|
||||
|
||||
for (let [key, value] of Object.entries(data.attrs|{}))
|
||||
for (let [key, value] of Object.entries(data.attrs | {}))
|
||||
this.setAttrValue(key, value)
|
||||
|
||||
delete data.attrs
|
||||
@@ -566,12 +586,12 @@ class Widget extends React.Component {
|
||||
// console.log("dragging event: ", event, dragElement)
|
||||
const container = dragElement.getAttribute("data-container")
|
||||
// TODO: check if the drop is allowed
|
||||
if (container === "canvas"){
|
||||
|
||||
if (container === "canvas") {
|
||||
|
||||
this.props.onAddChildWidget(this.__id, dragElement.getAttribute("data-widget-id"))
|
||||
|
||||
}else if (container === "sidebar"){
|
||||
|
||||
|
||||
} else if (container === "sidebar") {
|
||||
|
||||
this.props.onAddChildWidget(this.__id, null, true) // if dragged from the sidebar create the widget first
|
||||
|
||||
}
|
||||
@@ -611,7 +631,7 @@ class Widget extends React.Component {
|
||||
// NOTE: this line will prevent problem's such as self-drop or dropping inside its own children
|
||||
setTimeout(this.disablePointerEvents, 1)
|
||||
|
||||
this.setState({isDragging: true})
|
||||
this.setState({ isDragging: true })
|
||||
|
||||
}
|
||||
|
||||
@@ -621,22 +641,22 @@ class Widget extends React.Component {
|
||||
|
||||
// console.log("Drag entering...", dragEleType, draggedElement, this.droppableTags)
|
||||
// FIXME: the outer widget shouldn't be swallowed by inner widget
|
||||
if (draggedElement === this.elementRef.current){
|
||||
if (draggedElement === this.elementRef.current) {
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
setOverElement(e.currentTarget) // provide context to the provider
|
||||
|
||||
let showDrop = {
|
||||
allow: true,
|
||||
allow: true,
|
||||
show: true
|
||||
}
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
|
||||
if (allowDrop) {
|
||||
showDrop = {
|
||||
@@ -654,22 +674,22 @@ class Widget extends React.Component {
|
||||
this.setState({
|
||||
showDroppableStyle: showDrop
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
handleDragOver = (e, draggedElement) => {
|
||||
if (draggedElement === this.elementRef.current){
|
||||
if (draggedElement === this.elementRef.current) {
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
|
||||
if (allowDrop) {
|
||||
e.preventDefault() // NOTE: this is necessary to allow drop to take place
|
||||
@@ -677,15 +697,15 @@ class Widget extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
handleDropEvent = (e, draggedElement, widgetClass=null) => {
|
||||
handleDropEvent = (e, draggedElement, widgetClass = null) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
// FIXME: sometimes the elements showDroppableStyle is not gone, when dropping on the same widget
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
}, () => {
|
||||
console.log("droppable cleared: ", this.elementRef.current, this.state.showDroppableStyle)
|
||||
})
|
||||
@@ -693,51 +713,52 @@ class Widget extends React.Component {
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
|
||||
if (allowDrop) {
|
||||
if (!allowDrop) {
|
||||
return // prevent drop if the draggable element doesn't match
|
||||
}
|
||||
|
||||
// if (draggedElement === this.elementRef.current){
|
||||
// // prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
// return
|
||||
// }
|
||||
if (draggedElement === this.elementRef.current){
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
}
|
||||
|
||||
// let currentElement = e.currentTarget
|
||||
// while (currentElement) {
|
||||
// if (currentElement === draggedElement) {
|
||||
// // if the parent is dropped accidentally into the child don't allow drop
|
||||
// console.log("Dropped into a descendant element, ignoring drop")
|
||||
// return // Exit early to prevent the drop
|
||||
// }
|
||||
// currentElement = currentElement.parentElement // Traverse up to check ancestors
|
||||
// }
|
||||
let currentElement = e.currentTarget
|
||||
while (currentElement) {
|
||||
if (currentElement === draggedElement) {
|
||||
// if the parent is dropped accidentally into the child don't allow drop
|
||||
// console.log("Dropped into a descendant element, ignoring drop")
|
||||
return // Exit early to prevent the drop
|
||||
}
|
||||
currentElement = currentElement.parentElement // Traverse up to check ancestors
|
||||
}
|
||||
|
||||
const container = draggedElement.getAttribute("data-container")
|
||||
|
||||
const thisContainer = this.elementRef.current.getAttribute("data-container")
|
||||
// console.log("Dropped as swappable: ", e.target, this.swappableAreaRef.current.contains(e.target))
|
||||
// If swaparea is true, then it swaps instead of adding it as a child, also make sure that the parent widget(this widget) is on the widget and not on the canvas
|
||||
const swapArea = (this.swappableAreaRef.current.contains(e.target) && !this.innerAreaRef.current.contains(e.target) && thisContainer === WidgetContainer.WIDGET)
|
||||
const swapArea = (this.swappableAreaRef.current.contains(e.target) && !this.innerAreaRef.current.contains(e.target) && thisContainer === WidgetContainer.WIDGET)
|
||||
|
||||
// TODO: check if the drop is allowed
|
||||
if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)){
|
||||
if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)) {
|
||||
// console.log("Dropped on meee: ", swapArea, this.swappableAreaRef.current.contains(e.target), thisContainer)
|
||||
|
||||
this.props.onAddChildWidget({parentWidgetId: this.__id,
|
||||
dragElementID: draggedElement.getAttribute("data-widget-id"),
|
||||
swap: swapArea || false
|
||||
})
|
||||
|
||||
}else if (container === WidgetContainer.SIDEBAR){
|
||||
|
||||
|
||||
this.props.onAddChildWidget({
|
||||
parentWidgetId: this.__id,
|
||||
dragElementID: draggedElement.getAttribute("data-widget-id"),
|
||||
swap: swapArea || false
|
||||
})
|
||||
|
||||
} else if (container === WidgetContainer.SIDEBAR) {
|
||||
|
||||
// console.log("Dropped on Sidebar: ", this.__id)
|
||||
this.props.onCreateWidgetRequest(widgetClass, ({id, widgetRef}) => {
|
||||
this.props.onAddChildWidget({parentWidgetId: this.__id, dragElementID: id}) // if dragged from the sidebar create the widget first
|
||||
this.props.onCreateWidgetRequest(widgetClass, ({ id, widgetRef }) => {
|
||||
this.props.onAddChildWidget({ parentWidgetId: this.__id, dragElementID: id }) // if dragged from the sidebar create the widget first
|
||||
})
|
||||
|
||||
}
|
||||
@@ -751,23 +772,23 @@ class Widget extends React.Component {
|
||||
|
||||
if (!e.currentTarget.contains(draggedElement)) {
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
})
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
handleDragEnd = (callback) => {
|
||||
callback()
|
||||
this.setState({isDragging: false})
|
||||
this.setState({ isDragging: false })
|
||||
this.enablePointerEvents()
|
||||
}
|
||||
|
||||
disablePointerEvents = () => {
|
||||
|
||||
|
||||
if (this.elementRef.current)
|
||||
this.elementRef.current.style.pointerEvents = "none"
|
||||
}
|
||||
@@ -781,7 +802,7 @@ class Widget extends React.Component {
|
||||
* Note: you must implement this method in subclass, if you want children make sure to pass
|
||||
* {this.props.children}, to modify the style add this.state.widgetStyling
|
||||
*/
|
||||
renderContent() {
|
||||
renderContent() {
|
||||
// throw new NotImplementedError("render method has to be implemented")
|
||||
return (
|
||||
<div className="tw-w-full tw-h-full tw-p-2 tw-content-start tw-rounded-md tw-overflow-hidden" style={this.state.widgetStyling}>
|
||||
@@ -807,75 +828,75 @@ class Widget extends React.Component {
|
||||
height: `${this.state.size.height}px`,
|
||||
opacity: this.state.isDragging ? 0.3 : 1,
|
||||
}
|
||||
|
||||
// FIXME: if the parent container has tw-overflow-none, then the resizable indicator are also hidden
|
||||
return (
|
||||
|
||||
<DragContext.Consumer>
|
||||
{
|
||||
({draggedElement, widgetClass, onDragStart, onDragEnd, setOverElement}) => (
|
||||
({ draggedElement, widgetClass, onDragStart, onDragEnd, setOverElement }) => (
|
||||
|
||||
<div data-widget-id={this.__id}
|
||||
ref={this.elementRef}
|
||||
className="tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
style={outerStyle}
|
||||
data-draggable-type={this.getWidgetType()} // helps with droppable
|
||||
data-container={this.state.widgetContainer} // indicates how the canvas should handle dragging, one is sidebar other is canvas
|
||||
|
||||
draggable={this.state.dragEnabled}
|
||||
|
||||
onDragOver={(e) => this.handleDragOver(e, draggedElement)}
|
||||
onDrop={(e) => this.handleDropEvent(e, draggedElement, widgetClass)}
|
||||
<div data-widget-id={this.__id}
|
||||
ref={this.elementRef}
|
||||
className="tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
style={outerStyle}
|
||||
data-draggable-type={this.getWidgetType()} // helps with droppable
|
||||
data-container={this.state.widgetContainer} // indicates how the canvas should handle dragging, one is sidebar other is canvas
|
||||
|
||||
onDragEnter={(e) => this.handleDragEnter(e, draggedElement, setOverElement)}
|
||||
onDragLeave={(e) => this.handleDragLeave(e, draggedElement)}
|
||||
draggable={this.state.dragEnabled}
|
||||
|
||||
onDragStart={(e) => this.handleDragStart(e, onDragStart)}
|
||||
onDragEnd={(e) => this.handleDragEnd(onDragEnd)}
|
||||
>
|
||||
onDragOver={(e) => this.handleDragOver(e, draggedElement)}
|
||||
onDrop={(e) => this.handleDropEvent(e, draggedElement, widgetClass)}
|
||||
|
||||
onDragEnter={(e) => this.handleDragEnter(e, draggedElement, setOverElement)}
|
||||
onDragLeave={(e) => this.handleDragLeave(e, draggedElement)}
|
||||
|
||||
onDragStart={(e) => this.handleDragStart(e, onDragStart)}
|
||||
onDragEnd={(e) => this.handleDragEnd(onDragEnd)}
|
||||
>
|
||||
{/* FIXME: Swappable when the parent layout is flex/grid and gap is more, this trick won't work, add bg color to check */}
|
||||
{/* FIXME: Swappable, when the parent layout is gap is 0, it doesn't work well */}
|
||||
<div className="tw-relative tw-w-full tw-h-full tw-top-0 tw-left-0"
|
||||
>
|
||||
>
|
||||
<div className={`tw-absolute tw-top-[-5px] tw-left-[-5px]
|
||||
tw-border-1 tw-opacity-0 tw-border-solid tw-border-black
|
||||
tw-w-full tw-h-full
|
||||
tw-scale-[1.1] tw-opacity-1 tw-z-[-1] `}
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
ref={this.swappableAreaRef}
|
||||
|
||||
>
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
ref={this.swappableAreaRef}
|
||||
|
||||
>
|
||||
{/* helps with swappable: if the mouse is in this area while hovering/dropping, then swap */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-relative tw-w-full tw-h-full" ref={this.innerAreaRef}>
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
{
|
||||
// show drop style on drag hover
|
||||
this.state.showDroppableStyle.show &&
|
||||
<div className={`${this.state.showDroppableStyle.allow ? "tw-border-blue-600" : "tw-border-red-600"}
|
||||
<div className={`${this.state.showDroppableStyle.allow ? "tw-border-blue-600" : "tw-border-red-600"}
|
||||
tw-absolute tw-top-[-5px] tw-left-[-5px] tw-w-full tw-h-full tw-z-[2]
|
||||
tw-border-2 tw-border-dashed tw-rounded-lg tw-pointer-events-none
|
||||
|
||||
`}
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
<div className={`tw-absolute tw-bg-transparent tw-top-[-10px] tw-left-[-10px] tw-opacity-100
|
||||
tw-w-full tw-h-full tw-z-[-1]
|
||||
${this.state.selected ? 'tw-border-2 tw-border-solid tw-border-blue-500' : 'tw-hidden'}`}
|
||||
style={{
|
||||
width: "calc(100% + 20px)",
|
||||
height: "calc(100% + 20px)",
|
||||
}}
|
||||
>
|
||||
style={{
|
||||
width: "calc(100% + 20px)",
|
||||
height: "calc(100% + 20px)",
|
||||
}}
|
||||
>
|
||||
|
||||
<div className={`"tw-relative tw-w-full tw-h-full"`}> {/* ${this.state.isDragging ? "tw-pointer-events-none" : "tw-pointer-events-auto"} */}
|
||||
<EditableDiv value={this.state.widgetName} onChange={this.setWidgetName}
|
||||
@@ -892,9 +913,9 @@ class Widget extends React.Component {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.props.onWidgetResizing("nw")
|
||||
this.setState({dragEnabled: false})
|
||||
this.setState({ dragEnabled: false })
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
onMouseUp={() => this.setState({ dragEnabled: true })}
|
||||
/>
|
||||
<div
|
||||
className="tw-w-2 tw-h-2 tw-absolute tw--right-1 tw--top-1 tw-bg-blue-500"
|
||||
@@ -903,9 +924,9 @@ class Widget extends React.Component {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.props.onWidgetResizing("ne")
|
||||
this.setState({dragEnabled: false})
|
||||
this.setState({ dragEnabled: false })
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
onMouseUp={() => this.setState({ dragEnabled: true })}
|
||||
/>
|
||||
<div
|
||||
className="tw-w-2 tw-h-2 tw-absolute tw--left-1 tw--bottom-1 tw-bg-blue-500"
|
||||
@@ -914,9 +935,9 @@ class Widget extends React.Component {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.props.onWidgetResizing("sw")
|
||||
this.setState({dragEnabled: false})
|
||||
this.setState({ dragEnabled: false })
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
onMouseUp={() => this.setState({ dragEnabled: true })}
|
||||
/>
|
||||
<div
|
||||
className="tw-w-2 tw-h-2 tw-absolute tw--right-1 tw--bottom-1 tw-bg-blue-500"
|
||||
@@ -925,9 +946,9 @@ class Widget extends React.Component {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.props.onWidgetResizing("se")
|
||||
this.setState({dragEnabled: false})
|
||||
this.setState({ dragEnabled: false })
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
onMouseUp={() => this.setState({ dragEnabled: true })}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import Widget from "../../canvas/widgets/base"
|
||||
|
||||
import ButtonWidget from "./assets/widgets/button.png"
|
||||
import Button from "./widgets/button"
|
||||
import Frame from "./widgets/frame"
|
||||
import Input from "./widgets/input"
|
||||
import Label from "./widgets/label"
|
||||
import MainWindow from "./widgets/mainWindow"
|
||||
import TopLevel from "./widgets/toplevel"
|
||||
|
||||
|
||||
const TkinterSidebar = [
|
||||
@@ -16,25 +21,31 @@ const TkinterSidebar = [
|
||||
name: "Top Level",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Widget
|
||||
widgetClass: TopLevel
|
||||
},
|
||||
{
|
||||
name: "Frame",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Widget
|
||||
widgetClass: Frame
|
||||
},
|
||||
{
|
||||
name: "Label",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Label
|
||||
},
|
||||
{
|
||||
name: "Button",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Widget
|
||||
widgetClass: Button
|
||||
},
|
||||
{
|
||||
name: "Input",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Widget
|
||||
widgetClass: Input
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
104
src/frameworks/tkinter/widgets/button.js
Normal file
104
src/frameworks/tkinter/widgets/button.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class Button extends Widget{
|
||||
|
||||
static widgetType = "button"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// TODO: disable drop
|
||||
|
||||
this.droppableTags = {
|
||||
// TODO: exclude all
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetStyling("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
buttonLabel: {
|
||||
label: "Button Label",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Button label", maxLength: 100},
|
||||
value: "Button",
|
||||
onChange: (value) => this.setAttrValue("buttonLabel", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
buttonLabel: this.state.attrs.buttonLabel,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-border tw-border-solid tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start " style={this.state.widgetStyling}>
|
||||
{/* {this.props.children} */}
|
||||
<div className="tw-text-sm" style={{color: this.getAttrValue("styling.foregroundColor")}}>
|
||||
{this.getAttrValue("buttonLabel")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Button
|
||||
40
src/frameworks/tkinter/widgets/frame.js
Normal file
40
src/frameworks/tkinter/widgets/frame.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
|
||||
|
||||
class Frame extends Widget{
|
||||
|
||||
static widgetType = "frame"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#EDECEC")
|
||||
this.setWidgetName("frame")
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.state.widgetStyling}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Frame
|
||||
101
src/frameworks/tkinter/widgets/input.js
Normal file
101
src/frameworks/tkinter/widgets/input.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class Input extends Widget{
|
||||
|
||||
static widgetType = "input"
|
||||
// TODO: override the widgetName value
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
// TODO: exclude all
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetStyling("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
placeHolder: {
|
||||
label: "PlaceHolder",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: "placeholder text",
|
||||
onChange: (value) => this.setAttrValue("placeHolder", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start " style={this.state.widgetStyling}>
|
||||
<div className="tw-text-sm tw-text-gray-300">
|
||||
{this.getAttrValue("placeHolder")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Input
|
||||
102
src/frameworks/tkinter/widgets/label.js
Normal file
102
src/frameworks/tkinter/widgets/label.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class Label extends Widget{
|
||||
|
||||
static widgetType = "label"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
// TODO: exclude all
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetStyling("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
labelWidget: {
|
||||
label: "Text",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: "Label",
|
||||
onChange: (value) => this.setAttrValue("labelWidget", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
labelWidget: this.state.attrs.labelWidget,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start " style={this.state.widgetStyling}>
|
||||
{/* {this.props.children} */}
|
||||
<div className="tw-text-sm" style={{color: this.getAttrValue("styling.foregroundColor")}}>
|
||||
{this.getAttrValue("labelWidget")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Label
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CloseCircleFilled, ExpandOutlined, MinusCircleFilled } from "@ant-design/icons"
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class MainWindow extends Widget{
|
||||
@@ -10,21 +10,73 @@ class MainWindow extends Widget{
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media"]
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 700, height: 400 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
title: {
|
||||
label: "Window Title",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Window title", maxLength: 40},
|
||||
value: "Main Window",
|
||||
onChange: (value) => this.setAttrValue("title", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
title: this.state.attrs.title,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-flex tw-w-full tw-h-[25px] tw-bg-[#adadad] tw-shadow-md">
|
||||
<div className="tw-ml-auto tw-flex tw-gap-1 tw-p-1 tw-place-items-center">
|
||||
<div className="tw-flex tw-w-full tw-h-[25px] tw-bg-[#c7c7c7] tw-p-1
|
||||
tw-overflow-hidden tw-shadow-xl tw-place-items-center">
|
||||
<div className="tw-text-sm">{this.getAttrValue("title")}</div>
|
||||
<div className="tw-ml-auto tw-flex tw-gap-1 tw-place-items-center">
|
||||
<div className="tw-bg-yellow-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-blue-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
|
||||
99
src/frameworks/tkinter/widgets/toplevel.js
Normal file
99
src/frameworks/tkinter/widgets/toplevel.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class TopLevel extends Widget{
|
||||
|
||||
static widgetType = "toplevel"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
this.maxSize = { width: 2000, height: 2000 } // disables resizing above this number
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 450, height: 200 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
title: {
|
||||
label: "Window Title",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Window title", maxLength: 40},
|
||||
value: "Top level",
|
||||
onChange: (value) => this.setAttrValue("title", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
title: this.state.attrs.title,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-flex tw-w-full tw-h-[25px] tw-bg-[#c7c7c7] tw-p-1
|
||||
tw-overflow-hidden tw-shadow-xl tw-place-items-center">
|
||||
<div className="tw-text-sm">{this.getAttrValue("title")}</div>
|
||||
<div className="tw-ml-auto tw-flex tw-gap-1 tw-place-items-center">
|
||||
<div className="tw-bg-yellow-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-blue-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-red-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.state.widgetStyling}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default TopLevel
|
||||
Reference in New Issue
Block a user