working on code generation
This commit is contained in:
16
README.md
16
README.md
@@ -37,20 +37,23 @@ Build Python GUI's with the ease of Canva
|
||||
## Features
|
||||
* Framework agnostic - Can outputs code in multiple frameworks.
|
||||
* Easy to use.
|
||||
* Pre-built UI components
|
||||
* Plugins to extend 3rd party UI libraries
|
||||
* Generates Code.
|
||||
|
||||
## Roadmap
|
||||
Here are some of the upcoming features.
|
||||
* Treeview on the sidebar
|
||||
* Support for Event Handlers
|
||||
* Kivy Framework support
|
||||
* Pyqt/PySide Support
|
||||
* **Downloadable Electron app** and more.
|
||||
|
||||
To learn more/ see upcoming features visit [roadmap](./roadmap.md)
|
||||
To learn more/ see upcoming features visit [roadmap](./roadmap.md)
|
||||
|
||||
To stay in loop, subscribe to the free [newsletter](https://paulfreeman.substack.com/subscribe?utm_source=Github-Pybuilder)
|
||||
|
||||
|
||||
## License
|
||||
## License - Fund the development
|
||||
|
||||
To support open-source and development of this tool and upcoming free open-source tools and libraries, consider buying a one-time license.
|
||||
|
||||
@@ -128,6 +131,13 @@ This is meant for business usecases, you can use the code even for commercial us
|
||||
* All code generated by the builder tools are free to use for commercial and non-commercial purposes. If you are using this for a startup or your business you'll need to get a commercial license.
|
||||
|
||||
|
||||
## Some of my other open-source
|
||||
|
||||
* [Awesome Landing pages](https://github.com/PaulleDemon/awesome-landing-pages)
|
||||
* [Hover Preview](https://github.com/PaulleDemon/Hover-Preview)
|
||||
* [Font Tester](https://github.com/PaulleDemon/font-tester-chrome)
|
||||
* [Django SaaS Boilerplate](https://github.com/PaulleDemon/Django-SAAS-Boilerplate)
|
||||
|
||||
|
||||
## Author
|
||||
* Paul
|
||||
|
||||
150
src/App.js
150
src/App.js
@@ -1,8 +1,8 @@
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
import { LayoutFilled, ProductFilled, CloudUploadOutlined } from "@ant-design/icons"
|
||||
import { DndContext, useSensors, useSensor, PointerSensor, closestCorners, DragOverlay, rectIntersection } from '@dnd-kit/core'
|
||||
import { snapCenterToCursor } from '@dnd-kit/modifiers'
|
||||
// import { DndContext, useSensors, useSensor, PointerSensor, closestCorners, DragOverlay, rectIntersection } from '@dnd-kit/core'
|
||||
// import { snapCenterToCursor } from '@dnd-kit/modifiers'
|
||||
|
||||
import Canvas from './canvas/canvas'
|
||||
import Header from './components/header'
|
||||
@@ -10,13 +10,12 @@ import Sidebar from './sidebar/sidebar'
|
||||
import UploadsContainer from './sidebar/uploadsContainer'
|
||||
import WidgetsContainer from './sidebar/widgetsContainer'
|
||||
|
||||
import Widget from './canvas/widgets/base'
|
||||
import { DraggableWidgetCard } from './components/cards'
|
||||
import { DragProvider } from './components/draggable/draggableContext'
|
||||
import { ActiveWidgetProvider } from './canvas/activeWidgetContext'
|
||||
import TkinterWidgets from './frameworks/tkinter/sidebarWidgets'
|
||||
import PluginsContainer from './sidebar/pluginsContainer'
|
||||
import TkinterPluginWidgets from './frameworks/tkinter/sidebarPlugins'
|
||||
import FrameWorks from './constants/frameworks'
|
||||
import generateTkinterCode from './frameworks/tkinter/engine/code'
|
||||
|
||||
|
||||
function App() {
|
||||
@@ -25,22 +24,16 @@ function App() {
|
||||
* @type {Canvas | null>}
|
||||
*/
|
||||
const canvasRef = useRef()
|
||||
const widgetOverlayRef = useRef()
|
||||
const [initialPosition, setInitialPosition] = useState({ x: 0, y: 0 })
|
||||
|
||||
const [projectName, setProjectName] = useState('untitled project')
|
||||
const [UIFramework, setUIFramework] = useState(FrameWorks.TKINTER)
|
||||
|
||||
const [uploadedAssets, setUploadedAssets] = useState([]) // a global storage for assets, since redux can't store files(serialize files)
|
||||
|
||||
const [dropAnimation, setDropAnimation] = useState(null)
|
||||
|
||||
const [sidebarWidgets, setSidebarWidgets] = useState(TkinterWidgets || [])
|
||||
const [canvasWidgets, setCanvasWidgets] = useState([]) // contains the reference to the widgets inside the canvas
|
||||
|
||||
const [activeSidebarWidget, setActiveSidebarWidget] = useState(null) // helps with the dnd overlay
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor)
|
||||
)
|
||||
const [canvasWidgets, setCanvasWidgets] = useState([]) // contains the reference to the widgets inside the canvas
|
||||
const [canvasWidgetRefs, setCanvasWidgetRefs] = useState([])
|
||||
|
||||
const sidebarTabs = [
|
||||
{
|
||||
@@ -61,87 +54,112 @@ function App() {
|
||||
}
|
||||
]
|
||||
|
||||
const handleDragStart = (event) => {
|
||||
console.log("Drag start: ", event)
|
||||
const draggedItem = sidebarWidgets.find((item) => item.name === event.active.id)
|
||||
setActiveSidebarWidget(draggedItem)
|
||||
// const handleDragStart = (event) => {
|
||||
// console.log("Drag start: ", event)
|
||||
// const draggedItem = sidebarWidgets.find((item) => item.name === event.active.id)
|
||||
// setActiveSidebarWidget(draggedItem)
|
||||
|
||||
const activeItemElement = widgetOverlayRef.current
|
||||
// const activeItemElement = widgetOverlayRef.current
|
||||
|
||||
if (activeItemElement) {
|
||||
const rect = activeItemElement.getBoundingClientRect()
|
||||
// if (activeItemElement) {
|
||||
// const rect = activeItemElement.getBoundingClientRect()
|
||||
|
||||
// Store the initial position of the dragged element
|
||||
setInitialPosition({
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
})
|
||||
}
|
||||
}
|
||||
// // Store the initial position of the dragged element
|
||||
// setInitialPosition({
|
||||
// x: rect.left,
|
||||
// y: rect.top,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
const handleDragMove = (event) => {
|
||||
// const handleDragMove = (event) => {
|
||||
|
||||
// console.log("drag move: ", event)
|
||||
}
|
||||
// // console.log("drag move: ", event)
|
||||
// }
|
||||
|
||||
const handleDragEnd = (event) => {
|
||||
// add items to canvas from sidebar
|
||||
// const handleDragEnd = (event) => {
|
||||
// // add items to canvas from sidebar
|
||||
|
||||
const {active, over, delta, activatorEvent} = event
|
||||
// const {active, over, delta, activatorEvent} = event
|
||||
|
||||
const widgetItem = active.data.current?.title
|
||||
const activeItemElement = widgetOverlayRef.current
|
||||
// const widgetItem = active.data.current?.title
|
||||
// const activeItemElement = widgetOverlayRef.current
|
||||
|
||||
|
||||
console.log("ended: ", activatorEvent.clientX, activatorEvent.clientY)
|
||||
// console.log("over: ", active, over, activeItemElement)
|
||||
if (over?.id !== "canvas-droppable" || !widgetItem) {
|
||||
setDropAnimation({ duration: 250, easing: "ease" })
|
||||
return
|
||||
}
|
||||
setDropAnimation(null)
|
||||
// console.log("ended: ", activatorEvent.clientX, activatorEvent.clientY)
|
||||
// // console.log("over: ", active, over, activeItemElement)
|
||||
// if (over?.id !== "canvas-droppable" || !widgetItem) {
|
||||
// setDropAnimation({ duration: 250, easing: "ease" })
|
||||
// return
|
||||
// }
|
||||
// setDropAnimation(null)
|
||||
|
||||
// Get widget dimensions (assuming you have a way to get these)
|
||||
const widgetWidth = activeItemElement.offsetWidth; // Adjust this based on how you get widget size
|
||||
const widgetHeight = activeItemElement.offsetHeight; // Adjust this based on how you get widget size
|
||||
// // Get widget dimensions (assuming you have a way to get these)
|
||||
// const widgetWidth = activeItemElement.offsetWidth; // Adjust this based on how you get widget size
|
||||
// const widgetHeight = activeItemElement.offsetHeight; // Adjust this based on how you get widget size
|
||||
|
||||
|
||||
const canvasContainerRect = canvasRef.current.getCanvasContainerBoundingRect()
|
||||
const canvasTranslate = canvasRef.current.getCanvasTranslation()
|
||||
const zoom = canvasRef.current.getZoom()
|
||||
// const canvasContainerRect = canvasRef.current.getCanvasContainerBoundingRect()
|
||||
// const canvasTranslate = canvasRef.current.getCanvasTranslation()
|
||||
// const zoom = canvasRef.current.getZoom()
|
||||
|
||||
let finalPosition = {
|
||||
x: (initialPosition.x + delta.x - canvasContainerRect.x - canvasTranslate.x) / zoom - (widgetWidth / 2),
|
||||
y: (initialPosition.y + delta.y - canvasContainerRect.y - canvasTranslate.y) / zoom - (widgetHeight / 2),
|
||||
}
|
||||
// let finalPosition = {
|
||||
// x: (initialPosition.x + delta.x - canvasContainerRect.x - canvasTranslate.x) / zoom - (widgetWidth / 2),
|
||||
// y: (initialPosition.y + delta.y - canvasContainerRect.y - canvasTranslate.y) / zoom - (widgetHeight / 2),
|
||||
// }
|
||||
|
||||
|
||||
// find the center of the active widget then set the final position
|
||||
// // find the center of the active widget then set the final position
|
||||
|
||||
// finalPosition = {
|
||||
// finalPosition
|
||||
// }
|
||||
// // finalPosition = {
|
||||
// // finalPosition
|
||||
// // }
|
||||
|
||||
console.log("drop position: ", "delta: ", delta, "activator", finalPosition, canvasTranslate,)
|
||||
// console.log("drop position: ", "delta: ", delta, "activator", finalPosition, canvasTranslate,)
|
||||
|
||||
canvasRef.current.addWidget(Widget, ({id, widgetRef}) => {
|
||||
widgetRef.current.setPos(finalPosition.x, finalPosition.y)
|
||||
// widgetRef.current.setPos(10, 10)
|
||||
})
|
||||
// canvasRef.current.addWidget(Widget, ({id, widgetRef}) => {
|
||||
// widgetRef.current.setPos(finalPosition.x, finalPosition.y)
|
||||
// // widgetRef.current.setPos(10, 10)
|
||||
// })
|
||||
|
||||
setActiveSidebarWidget(null)
|
||||
// setActiveSidebarWidget(null)
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
const handleWidgetAddedToCanvas = (widgets) => {
|
||||
console.log("canvas ref: ", canvasRef)
|
||||
setCanvasWidgets(widgets)
|
||||
}
|
||||
|
||||
const handleCodeGen = () => {
|
||||
|
||||
if (UIFramework === FrameWorks.TKINTER){
|
||||
generateTkinterCode(projectName, canvasRef.current.getWidgets() || [], canvasRef.current.widgetRefs || [])
|
||||
}
|
||||
}
|
||||
|
||||
const handleFrameworkChange = (framework) => {
|
||||
|
||||
if (framework === UIFramework) return
|
||||
|
||||
canvasRef?.current?.clearCanvas()
|
||||
|
||||
setUIFramework(framework)
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tw-w-full tw-h-[100vh] tw-flex tw-flex-col tw-bg-primaryBg">
|
||||
<Header className="tw-h-[6vh]"/>
|
||||
<Header className="tw-h-[6vh]" onExportClick={handleCodeGen}
|
||||
projectName={projectName} onProjectNameChange={setProjectName}
|
||||
framework={UIFramework} onFrameworkChange={handleFrameworkChange}/>
|
||||
|
||||
{/* <Modal title={"FrameWork Change"} open={isModalOpen} onClose={handleCancel}
|
||||
okText={"Yes"}
|
||||
onOk={handleOk} okType={okButtonType} onCancel={handleCancel}>
|
||||
<p>Are you sure you want to change the framework? This will clear the canvas.</p>
|
||||
</Modal> */}
|
||||
|
||||
<DragProvider>
|
||||
<div className="tw-w-full tw-h-[94vh] tw-flex">
|
||||
<Sidebar tabs={sidebarTabs}/>
|
||||
|
||||
@@ -823,7 +823,7 @@ class Canvas extends React.Component {
|
||||
initialData: {
|
||||
...dragData,
|
||||
positionType: parentLayout === Layouts.PLACE ? PosType.ABSOLUTE : PosType.NONE,
|
||||
parentLayout: parentLayout,
|
||||
parentLayout: parentWidget.getLayout() || null, // pass everything about the parent layout
|
||||
zIndex: 0,
|
||||
pos: {x: finalPosition.x, y: finalPosition.y},
|
||||
widgetContainer: WidgetContainer.WIDGET
|
||||
|
||||
@@ -14,10 +14,11 @@ import WidgetContainer from "../constants/containers"
|
||||
import { DragContext } from "../../components/draggable/draggableContext"
|
||||
import { isNumeric, removeKeyFromObject } from "../../utils/common"
|
||||
|
||||
// FIXME: make it possible to have fit-width and height
|
||||
|
||||
// TODO: make it possible to apply widgetInnerStyle on load
|
||||
|
||||
// FIXME: the drag drop indicator is not going invisible if the drop happens on the child
|
||||
|
||||
const ATTRS_KEYS = ['value', 'label', 'tool', 'onChange', 'toolProps'] // these are attrs keywords, don't use these keywords as keys while defining the attrs property
|
||||
|
||||
|
||||
@@ -162,10 +163,14 @@ class Widget extends React.Component {
|
||||
this.getWidgetType = this.getWidgetType.bind(this)
|
||||
this.getBoundingRect = this.getBoundingRect.bind(this)
|
||||
|
||||
this.getAttrValue = this.getAttrValue.bind(this)
|
||||
this.getLayout = this.getLayout.bind(this)
|
||||
this.getParentLayout = this.getParentLayout.bind(this)
|
||||
|
||||
this.getAttrValue = this.getAttrValue.bind(this)
|
||||
this.getToolbarAttrs = this.getToolbarAttrs.bind(this)
|
||||
|
||||
this.generateCode = this.generateCode.bind(this)
|
||||
|
||||
// this.openRenaming = this.openRenaming.bind(this)
|
||||
|
||||
this.isSelected = this.isSelected.bind(this)
|
||||
@@ -337,7 +342,7 @@ class Widget extends React.Component {
|
||||
return this.constructor.requiredImports
|
||||
}
|
||||
|
||||
getCode = () => {
|
||||
generateCode(){
|
||||
throw new NotImplementedError("Get Code must be implemented by the subclass")
|
||||
}
|
||||
|
||||
@@ -534,12 +539,15 @@ class Widget extends React.Component {
|
||||
|
||||
/**
|
||||
* inform the child about the parent layout changes
|
||||
* @param {Layouts} layout
|
||||
* @param {Layouts} parentLayout
|
||||
*/
|
||||
setParentLayout(layout){
|
||||
setParentLayout(parentLayout){
|
||||
|
||||
const {layout, direction, gap} = parentLayout
|
||||
|
||||
|
||||
let updates = {
|
||||
parentLayout: layout,
|
||||
parentLayout: parentLayout,
|
||||
}
|
||||
|
||||
if (layout === Layouts.FLEX || layout === Layouts.GRID){
|
||||
@@ -556,16 +564,14 @@ class Widget extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Parent layout updated: ", updates)
|
||||
|
||||
this.setState(updates)
|
||||
}
|
||||
|
||||
getParentLayout = () => {
|
||||
getParentLayout(){
|
||||
return this.state.parentLayout
|
||||
}
|
||||
|
||||
getLayout = () => {
|
||||
getLayout(){
|
||||
|
||||
return this.state?.attrs?.layout?.value || Layouts.FLEX
|
||||
}
|
||||
@@ -726,17 +732,17 @@ class Widget extends React.Component {
|
||||
|
||||
|
||||
let layoutUpdates = {
|
||||
parentLayout: parentLayout
|
||||
parentLayout: parentLayout.layout || null
|
||||
}
|
||||
|
||||
if (parentLayout === Layouts.FLEX || parentLayout === Layouts.GRID){
|
||||
if (parentLayout?.layout === Layouts.FLEX || parentLayout?.layout === Layouts.GRID){
|
||||
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.NONE
|
||||
}
|
||||
|
||||
}else if (parentLayout === Layouts.PLACE){
|
||||
}else if (parentLayout?.layout === Layouts.PLACE){
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.ABSOLUTE
|
||||
@@ -987,7 +993,7 @@ class Widget extends React.Component {
|
||||
|
||||
// if (!e.currentTarget.contains(draggedElement)) {
|
||||
if (!isInBoundingBox) {
|
||||
// FIXME: if the mouse pointer is over this widget's child, then droppable from here
|
||||
// FIXME: if the mouse pointer is over this widget's child, then droppable style should be invisible
|
||||
// only if the dragging element is not within the rect of this element remove the dragging rect
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
|
||||
@@ -1,37 +1,42 @@
|
||||
import { useState } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { Select, Input, Button } from "antd"
|
||||
import { DownloadOutlined, DownOutlined } from "@ant-design/icons"
|
||||
import FrameWorks from "../constants/frameworks"
|
||||
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'tkinter',
|
||||
value: FrameWorks.TKINTER,
|
||||
label: 'tkinter',
|
||||
},
|
||||
{
|
||||
key: 'customtk',
|
||||
value: FrameWorks.CUSTOMTK,
|
||||
label: 'customtk',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
function Header(props){
|
||||
function Header({projectName, onProjectNameChange, framework, onFrameworkChange,
|
||||
onExportClick, className=''}){
|
||||
|
||||
const [projectName, setProjectName] = useState("project")
|
||||
|
||||
return (
|
||||
<div className={`tw-w-full tw-bg-primaryBg tw-p-2 tw-flex tw-place-items-center
|
||||
${props.className||''}`}>
|
||||
${className||''}`}>
|
||||
|
||||
<Select
|
||||
defaultValue={"tkinter"}
|
||||
// defaultValue={framework}
|
||||
value={framework}
|
||||
options={items}
|
||||
// onSelect={(key) => {console.log("value: ", key); onFrameworkChange(key); }}
|
||||
onChange={(key) => {onFrameworkChange(key)}}
|
||||
className="tw-min-w-[150px]"
|
||||
/>
|
||||
|
||||
<div className="tw-ml-auto tw-flex tw-gap-2 tw-place-content-center">
|
||||
<Input value={projectName} onChange={(e) => setProjectName(e.target.value)} placeholder="project name"/>
|
||||
<Button icon={<DownloadOutlined />} >
|
||||
<Input value={projectName} onChange={(e) => onProjectNameChange(e.target.value)} placeholder="project name"/>
|
||||
<Button icon={<DownloadOutlined />} onClick={onExportClick}>
|
||||
Export code
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,6 @@ export const ButtonModal = ({ message, title, okText="OK", onOk, onCancel, okBut
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalOpen(false)
|
||||
console.log("Ok pressed")
|
||||
if (onOk){
|
||||
onOk()
|
||||
}
|
||||
@@ -30,12 +29,12 @@ export const ButtonModal = ({ message, title, okText="OK", onOk, onCancel, okBut
|
||||
e.stopPropagation()
|
||||
|
||||
setIsModalOpen(false)
|
||||
console.log("cancel pressed")
|
||||
|
||||
if (onCancel){
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div onClick={showModal}>
|
||||
{children}
|
||||
|
||||
13
src/constants/frameworks.js
Normal file
13
src/constants/frameworks.js
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
const FrameWorks = {
|
||||
|
||||
TKINTER: 'tkinter',
|
||||
CUSTOMTK: 'customTk',
|
||||
KIVY: 'kivy',
|
||||
PYSIDE: 'PySide',
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default FrameWorks
|
||||
33
src/frameworks/tkinter/engine/code.js
Normal file
33
src/frameworks/tkinter/engine/code.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import MainWindow from "../widgets/mainWindow"
|
||||
|
||||
import { message } from "antd"
|
||||
|
||||
|
||||
async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[]){
|
||||
|
||||
console.log("widgetList and refs", projectName, widgetList, widgetRefs)
|
||||
|
||||
let mainWindowCount = 0
|
||||
|
||||
for (let widget of widgetList){
|
||||
if (widget.widgetType === MainWindow){
|
||||
mainWindowCount += 1
|
||||
}
|
||||
|
||||
if (mainWindowCount > 1){
|
||||
message.error("Multiple instances of Main window found, delete one and try again.")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mainWindowCount === 0){
|
||||
message.error("Aborting. No instances of Main window found. Add one and try again")
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default generateTkinterCode
|
||||
@@ -1,16 +1,46 @@
|
||||
import { Layouts, PosType } from "../../../canvas/constants/layouts"
|
||||
import Tools from "../../../canvas/constants/tools";
|
||||
import Widget from "../../../canvas/widgets/base";
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
|
||||
|
||||
|
||||
class TkinterBase extends Widget {
|
||||
|
||||
|
||||
setParentLayout(layout){
|
||||
|
||||
static requiredImports = ['import tkinter as tk']
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.getLayoutCode = this.getLayoutCode.bind(this)
|
||||
}
|
||||
|
||||
getLayoutCode(){
|
||||
const {layout: parentLayout, direction, gap} = this.getParentLayout()
|
||||
|
||||
let layoutManager = `pack()`
|
||||
|
||||
if (parentLayout === Layouts.FLEX){
|
||||
layoutManager = `pack(${direction === "horizontal"? "tk.LEFT" : "tk.TOP"})`
|
||||
}else if (parentLayout === Layouts.GRID){
|
||||
const row = this.getAttrValue("gridManager.row")
|
||||
const col = this.getAttrValue("gridManager.col")
|
||||
layoutManager = `grid(row=${row}, col=${col})`
|
||||
}else{
|
||||
// FIXME: position may not be correct
|
||||
layoutManager = `place(x=${this.state.pos.x}, y=${this.state.pos.y})`
|
||||
}
|
||||
|
||||
return layoutManager
|
||||
}
|
||||
|
||||
setParentLayout(parentLayout){
|
||||
console.log("parent layout: ", parentLayout)
|
||||
|
||||
const {layout, direction, gap} = parentLayout
|
||||
|
||||
// show attributes related to the layout manager
|
||||
let updates = {
|
||||
parentLayout: layout,
|
||||
parentLayout: parentLayout,
|
||||
}
|
||||
|
||||
this.removeAttr("gridManager")
|
||||
@@ -21,7 +51,7 @@ class TkinterBase extends Widget {
|
||||
positionType: PosType.NONE
|
||||
}
|
||||
|
||||
if (layout === Layouts.GRID) {
|
||||
if (parentLayout === Layouts.GRID) {
|
||||
// Set attributes related to grid layout manager
|
||||
updates = {
|
||||
...updates,
|
||||
@@ -145,14 +175,14 @@ class TkinterBase extends Widget {
|
||||
parentLayout: parentLayout
|
||||
}
|
||||
|
||||
if (parentLayout === Layouts.FLEX || parentLayout === Layouts.GRID){
|
||||
if (parentLayout.layout === Layouts.FLEX || parentLayout.layout === Layouts.GRID){
|
||||
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.NONE
|
||||
}
|
||||
|
||||
}else if (parentLayout === Layouts.PLACE){
|
||||
}else if (parentLayout.layout === Layouts.PLACE){
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.ABSOLUTE
|
||||
|
||||
@@ -10,7 +10,7 @@ class Frame extends TkinterBase{
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media", "toplevel"]
|
||||
exclude: ["image", "video", "media", "toplevel", "main_window"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
|
||||
@@ -2,6 +2,7 @@ import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
import TkinterBase from "./base"
|
||||
import { Layouts } from "../../../canvas/constants/layouts"
|
||||
|
||||
|
||||
class Label extends TkinterBase{
|
||||
@@ -45,6 +46,16 @@ class Label extends TkinterBase{
|
||||
}
|
||||
}
|
||||
|
||||
generateCode(parent){
|
||||
|
||||
const label = this.getAttrValue("labelWidget")
|
||||
|
||||
return (`
|
||||
${this.getWidgetName()} = tk.Label(master=${parent}, text="${label}")
|
||||
${this.getWidgetName()}.${this.getLayoutCode()}
|
||||
`)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff0")
|
||||
|
||||
@@ -36,6 +36,13 @@ class MainWindow extends Widget{
|
||||
this.setWidgetName("main")
|
||||
}
|
||||
|
||||
generateCode(parent){
|
||||
|
||||
return (`
|
||||
${this.getWidgetName()} = tk.Tk()
|
||||
`)
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ function Premium({ children, className = "" }) {
|
||||
<div onClick={onClick} className={`${className}`}>
|
||||
{children}
|
||||
<Modal
|
||||
title={<h3 className="tw-text-xl tw-font-medium">Buy Pre-order one Time License</h3>}
|
||||
title={<h3 className="tw-text-xl tw-font-medium">Fund development. Pre-order one Time License</h3>}
|
||||
style={{ zIndex: 14000, gap: '10px', maxWidth: '80vw', placeItems: "center" }}
|
||||
onCancel={onClose}
|
||||
centered
|
||||
@@ -33,8 +33,8 @@ function Premium({ children, className = "" }) {
|
||||
open={premiumModalOpen}
|
||||
>
|
||||
<div className="tw-mt-5 tw-text-lg tw-max-w-[850px] tw-w-full ">
|
||||
I am Paul, an open-source dev, funding open-source projects by providing custom works.
|
||||
If you find this tool useful and want to support its development, consider buying a <b>one time license</b>.
|
||||
I am Paul, a self-funded open-source dev.
|
||||
If you find this tool useful and want to fund and support it's development, consider buying a <b>one time license</b>.
|
||||
<br />
|
||||
<br />
|
||||
By buying pre-order license, you get advance features, priority support, early access, upcoming features, and more.
|
||||
@@ -89,6 +89,10 @@ function Premium({ children, className = "" }) {
|
||||
<i className="bi bi-x-circle-fill tw-text-red-600 tw-text-base"></i>
|
||||
<span>Load plugins locally</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-red-600 tw-text-base"></i>
|
||||
<span>Load local UI templates</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-red-600 tw-text-base"></i>
|
||||
<span>Dark theme</span>
|
||||
@@ -146,6 +150,10 @@ function Premium({ children, className = "" }) {
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Load plugins locally</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Load local UI templates</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Dark theme</span>
|
||||
@@ -221,6 +229,10 @@ function Premium({ children, className = "" }) {
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Load plugins locally</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Load local UI templates</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Dark theme</span>
|
||||
|
||||
Reference in New Issue
Block a user