From b8a530254dc1bb234452a690e2e26acd17681a83 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 11 Jun 2025 00:17:58 +0200 Subject: [PATCH] Blockly review --- Blockly/Delphi/MainUnit.fmx | 97 ++++---- Blockly/Delphi/MainUnit.pas | 13 +- Blockly/blockly_llvm.html | 192 ---------------- Blockly/index.html | 38 ++++ Blockly/script.js | 429 ++++++++++++++++++++++++++++++++++++ Blockly/style.css | 116 ++++++++++ 6 files changed, 644 insertions(+), 241 deletions(-) delete mode 100644 Blockly/blockly_llvm.html create mode 100644 Blockly/index.html create mode 100644 Blockly/script.js create mode 100644 Blockly/style.css diff --git a/Blockly/Delphi/MainUnit.fmx b/Blockly/Delphi/MainUnit.fmx index 81ddf10..bc7a112 100644 --- a/Blockly/Delphi/MainUnit.fmx +++ b/Blockly/Delphi/MainUnit.fmx @@ -1,7 +1,7 @@ object MainForm: TMainForm Left = 0 Top = 0 - Caption = 'Form1' + Caption = 'Blockly LLVM IR Generator' ClientHeight = 872 ClientWidth = 1010 FormFactor.Width = 320 @@ -12,57 +12,66 @@ object MainForm: TMainForm DesignerMasterStyle = 0 object WebBrowser1: TWebBrowser Align = Top - Anchors = [akLeft, akTop, akRight, akBottom] + Anchors = [akLeft, akTop] Size.Width = 1010.000000000000000000 - Size.Height = 633.000000000000000000 + Size.Height = 625.000000000000000000 Size.PlatformDefault = False WindowsEngine = EdgeOnly OnDidFinishLoad = WebBrowser1DidFinishLoad - end - object LauchSiteButton: TButton - Anchors = [akLeft, akBottom] - Position.X = 24.000000000000000000 - Position.Y = 744.000000000000000000 - TabOrder = 1 - Text = 'Lauch Site' - TextSettings.Trimming = None - OnClick = LauchSiteButtonClick - end - object SaveWorkspaceButton: TButton - Anchors = [akLeft, akBottom] - Position.X = 24.000000000000000000 - Position.Y = 774.000000000000000000 - Size.Width = 97.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - TabOrder = 2 - Text = 'Save Workspace' - TextSettings.Trimming = None - OnClick = SaveWorkspaceButtonClick - end - object GenerateCodeButton: TButton - Anchors = [akLeft, akBottom] - Position.X = 24.000000000000000000 - Position.Y = 804.000000000000000000 - Size.Width = 97.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - TabOrder = 3 - Text = 'Generate Code' - TextSettings.Trimming = None - OnClick = GenerateCodeButtonClick + object ToolBar1: TToolBar + Size.Width = 1010.000000000000000000 + Size.Height = 40.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object RefreshButton: TSpeedButton + Align = Left + Size.Width = 80.000000000000000000 + Size.Height = 40.000000000000000000 + Size.PlatformDefault = False + Text = 'Refresh' + TextSettings.Trimming = None + OnClick = LauchSiteButtonClick + end + object SaveWorkspaceButton: TSpeedButton + Align = Left + Position.X = 80.000000000000000000 + Size.Width = 80.000000000000000000 + Size.Height = 40.000000000000000000 + Size.PlatformDefault = False + Text = 'Save' + TextSettings.Trimming = None + OnClick = SaveWorkspaceButtonClick + end + object GenerateCodeButton: TSpeedButton + Align = Left + Position.X = 160.000000000000000000 + Size.Width = 80.000000000000000000 + Size.Height = 40.000000000000000000 + Size.PlatformDefault = False + Text = 'Generate' + TextSettings.Trimming = None + OnClick = GenerateCodeButtonClick + end + end end object Memo: TMemo Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] DataDetectorTypes = [] - Anchors = [akRight, akBottom] - Position.X = 144.000000000000000000 - Position.Y = 641.000000000000000000 - Size.Width = 841.000000000000000000 - Size.Height = 223.000000000000000000 + Align = Client + Size.Width = 1010.000000000000000000 + Size.Height = 239.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + Viewport.Width = 1006.000000000000000000 + Viewport.Height = 235.000000000000000000 + end + object Splitter1: TSplitter + Align = Top + Cursor = crVSplit + MinSize = 20.000000000000000000 + Position.Y = 625.000000000000000000 + Size.Width = 1010.000000000000000000 + Size.Height = 8.000000000000000000 Size.PlatformDefault = False - TabOrder = 4 - Viewport.Width = 837.000000000000000000 - Viewport.Height = 219.000000000000000000 end end diff --git a/Blockly/Delphi/MainUnit.pas b/Blockly/Delphi/MainUnit.pas index 9717d48..73ff336 100644 --- a/Blockly/Delphi/MainUnit.pas +++ b/Blockly/Delphi/MainUnit.pas @@ -11,7 +11,7 @@ uses const // Pfad zur HTML-Datei - PATH_BLOCKLY = 'T:\Myc\Blockly\blockly_LLVM.html'; + PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; // Pfade zu den LLVM-Werkzeugen CLANG_PATH = 'clang.exe'; @@ -20,10 +20,12 @@ const type TMainForm = class( TForm ) WebBrowser1: TWebBrowser; - LauchSiteButton: TButton; - SaveWorkspaceButton: TButton; - GenerateCodeButton: TButton; Memo: TMemo; + Splitter1: TSplitter; + ToolBar1: TToolBar; + RefreshButton: TSpeedButton; + SaveWorkspaceButton: TSpeedButton; + GenerateCodeButton: TSpeedButton; procedure FormCreate( Sender: TObject ); procedure FormDestroy( Sender: TObject ); procedure LauchSiteButtonClick( Sender: TObject ); @@ -83,6 +85,7 @@ end; procedure TMainForm.FormCreate( Sender: TObject ); begin TLLVMRunner.Log := Memo.Lines; + LauchSiteButtonClick(Self); end; procedure TMainForm.FormDestroy( Sender: TObject ); @@ -104,7 +107,7 @@ var cmdLine: string; buffer: TBytes; bytesRead: Cardinal; - output, errors: string; + output: string; begin // Pipe für StdOut und StdErr erstellen sa.nLength := SizeOf( TSecurityAttributes ); diff --git a/Blockly/blockly_llvm.html b/Blockly/blockly_llvm.html deleted file mode 100644 index 854affb..0000000 --- a/Blockly/blockly_llvm.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - Blockly zu LLVM IR - Final - - - - -

LLVM IR (.dll) Generator

-

Bauen Sie links Ihre Logik. Rechts erscheint der LLVM-Code, der zu einer 64-Bit DLL für Windows kompiliert werden kann.

- -
- - - -
- -
-
-
-

Erzeugter LLVM-Code:

- -
-
- - - - - - - - - diff --git a/Blockly/index.html b/Blockly/index.html new file mode 100644 index 0000000..4e14cdd --- /dev/null +++ b/Blockly/index.html @@ -0,0 +1,38 @@ + + + + + Blockly zu LLVM IR - Final + + + + + +

LLVM IR (.dll) Generator

+

Bauen Sie links Ihre Logik. Rechts erscheint der LLVM-Code, der zu einer 64-Bit DLL für Windows kompiliert werden kann.

+ +
+ + + +
+ +
+
+
+
+
+

Erzeugter LLVM-Code:

+ +
+
+
+ + + + + + + + + diff --git a/Blockly/script.js b/Blockly/script.js new file mode 100644 index 0000000..a85c642 --- /dev/null +++ b/Blockly/script.js @@ -0,0 +1,429 @@ +window.onload = function() { + + let workspace; + const isWebView = window.chrome && window.chrome.webview; + + // === LLVM-IR-GENERATOR (FINALE VERSION) === + Blockly.LLVM = new Blockly.Generator('LLVM'); + Blockly.LLVM.ORDER_ATOMIC = 0; + Blockly.LLVM.ORDER_NONE = 99; + + // --- Block-Definitionen für die Toolbox --- + Blockly.defineBlocksWithJsonArray([ + { + "type": "host_log_integer", + "message0": "Logge Integer %1", + "args0": [ + { + "type": "input_value", + "name": "VALUE", + "check": "Number" + } + ], + "previousStatement": null, + "nextStatement": null, + "colour": 290, + "tooltip": "Sendet einen Integer-Wert an den Delphi-Host.", + "helpUrl": "" + }, + { + "type": "current_datetime_utc", + "message0": "Aktueller UTC-Zeitstempel", + "output": "DateTimeUTC", + "colour": 120, + "tooltip": "Liefert den aktuellen Zeitstempel in UTC.", + "helpUrl": "" + }, + { + "type": "get_datetime_component", + "message0": "Hole %1 von Zeitstempel %2 in Zeitzone %3", + "args0": [ + { + "type": "field_dropdown", + "name": "COMPONENT", + "options": [ + ["Jahr", "YEAR"], + ["Monat", "MONTH"], + ["Tag", "DAY"], + ["Stunde", "HOUR"], + ["Minute", "MINUTE"], + ["Sekunde", "SECOND"] + ] + }, + { + "type": "input_value", + "name": "DATETIME", + "check": "DateTimeUTC" + }, + { + "type": "field_dropdown", + "name": "TIMEZONE", + "options": [ + ["UTC", "UTC"], + ["ET (New York)", "ET"], + ["WET (London)", "WET"], + ["CET (Frankfurt)", "CET"], + ["AEST (Sydney)", "AEST"], + ["JST (Tokyo)", "JST"] + ] + } + ], + "output": "Number", + "colour": 120, + "tooltip": "Extrahiert eine Komponente (Jahr, Monat, etc.) aus einem UTC-Zeitstempel und konvertiert sie in die angegebene Zeitzone.", + "helpUrl": "" + } + ]); + + // --- Generator-Implementierungen --- + + Blockly.LLVM.regCounter = 0; + Blockly.LLVM.newReg = function() { + return '%' + (Blockly.LLVM.regCounter++); + }; + + Blockly.LLVM.workspaceToCode = function(workspace) { + Blockly.LLVM.regCounter = 1; + Blockly.LLVM.generatedStrings = new Set(); + Blockly.LLVM.globalDeclarations = ''; + Blockly.LLVM.functionDeclarations = ''; + + const allVariables = workspace.getAllVariables(); + let varAllocations = ''; + if (allVariables.length > 0) { + allVariables.forEach(v => { + const safeVarName = v.name.replace(/ /g, '_'); + varAllocations += ` @${safeVarName} = common global i32 0\n`; + }); + } + + const topBlock = workspace.getTopBlocks(true)[0]; + const code = topBlock ? Blockly.LLVM.blockToCode(topBlock) : ''; + + let moduleCode = `target triple = "x86_64-pc-windows-msvc"\n\n`; + + moduleCode += `; Definition des DateTimeUTC-Structs\n`; + moduleCode += `%struct.DateTimeUTC = type { i32, i32, i32, i32, i32, i32 }\n\n`; + + if (Blockly.LLVM.globalDeclarations) { + moduleCode += '; Global string constants for timezones\n'; + moduleCode += Blockly.LLVM.globalDeclarations + '\n'; + } + + moduleCode += `; Host Callback Declarations\n`; + moduleCode += `declare void @get_current_datetime_utc(%struct.DateTimeUTC*)\n`; + moduleCode += `declare i32 @get_datetime_component_in_zone(%struct.DateTimeUTC*, i32, i8*)\n`; + + if (Blockly.LLVM.functionDeclarations) { + moduleCode += Blockly.LLVM.functionDeclarations + '\n'; + } + + moduleCode += `@gLogIntProc = common global void (i32)* null\n`; + moduleCode += `@gGetCurrentDateTimeUTCProc = common global void (%struct.DateTimeUTC*)* null\n`; + moduleCode += `@gGetComponentInZoneProc = common global i32 (%struct.DateTimeUTC*, i32, i8*)* null\n\n`; + + + if (varAllocations) { + moduleCode += '; Global Variables\n'; + moduleCode += varAllocations + '\n'; + } + + moduleCode += 'define dllexport void @StarteLogik(void (i32)* %LogIntProc, void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc) {\n'; + moduleCode += 'entry:\n'; + moduleCode += ' store void (i32)* %LogIntProc, void (i32)** @gLogIntProc\n'; + moduleCode += ' store void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, void (%struct.DateTimeUTC*)** @gGetCurrentDateTimeUTCProc\n'; + moduleCode += ' store i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc, i32 (%struct.DateTimeUTC*, i32, i8*)** @gGetComponentInZoneProc\n\n'; + + moduleCode += code.split('\n').map(line => line ? ' ' + line : '').join('\n'); + moduleCode += '\n ret void\n'; + moduleCode += '}\n'; + return moduleCode; + }; + + Blockly.LLVM.scrub_ = function(block, code) { + const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); + const nextCode = nextBlock ? Blockly.LLVM.blockToCode(nextBlock) : ''; + return code + nextCode; + }; + + Blockly.LLVM.forBlock['variables_set'] = function(block) { + const varName = block.workspace.getVariableById(block.getFieldValue('VAR')).name.replace(/ /g, '_'); + const value = Blockly.LLVM.valueToCode(block, 'VALUE', Blockly.LLVM.ORDER_ATOMIC) || '0'; + return `store i32 ${value}, i32* @${varName}\n`; + }; + + Blockly.LLVM.forBlock['math_change'] = function(block) { + const varName = block.workspace.getVariableById(block.getFieldValue('VAR')).name.replace(/ /g, '_'); + const delta = Blockly.LLVM.valueToCode(block, 'DELTA', Blockly.LLVM.ORDER_ATOMIC) || '0'; + + const loadReg = Blockly.LLVM.newReg(); + const addReg = Blockly.LLVM.newReg(); + + let code = ''; + code += `${loadReg} = load i32, i32* @${varName}\n`; + code += `${addReg} = add nsw i32 ${loadReg}, ${delta}\n`; + code += `store i32 ${addReg}, i32* @${varName}\n`; + return code; + }; + + Blockly.LLVM.forBlock['variables_get'] = function(block) { + const varName = block.workspace.getVariableById(block.getFieldValue('VAR')).name.replace(/ /g, '_'); + return ['@' + varName, Blockly.LLVM.ORDER_ATOMIC]; + }; + + Blockly.LLVM.forBlock['math_number'] = function(block) { + return [String(block.getFieldValue('NUM')), Blockly.LLVM.ORDER_ATOMIC]; + }; + + Blockly.LLVM.forBlock['host_log_integer'] = function(block) { + const valueCode = Blockly.LLVM.valueToCode(block, 'VALUE', Blockly.LLVM.ORDER_ATOMIC) || '0'; + + let valueReg; + let code = ''; + + if (valueCode.startsWith('@')) { + valueReg = Blockly.LLVM.newReg(); + code += `${valueReg} = load i32, i32* ${valueCode}\n`; + } else if (valueCode.startsWith('%')) { + valueReg = valueCode; + } else { + valueReg = valueCode; + } + + const logPtrReg = Blockly.LLVM.newReg(); + code += `${logPtrReg} = load void (i32)*, void (i32)** @gLogIntProc\n`; + code += `call void ${logPtrReg}(i32 ${valueReg})\n`; + return code; + }; + + Blockly.LLVM.forBlock['current_datetime_utc'] = function(block) { + const structPtrReg = Blockly.LLVM.newReg(); + const getUtcProcPtrReg = Blockly.LLVM.newReg(); + + let code = ` + ${structPtrReg} = alloca %struct.DateTimeUTC, align 8 + ${getUtcProcPtrReg} = load void (%struct.DateTimeUTC*)*, void (%struct.DateTimeUTC*)** @gGetCurrentDateTimeUTCProc\n + call void ${getUtcProcPtrReg}(%struct.DateTimeUTC* ${structPtrReg}) + `; + + return [code + structPtrReg, Blockly.LLVM.ORDER_ATOMIC]; + }; + + Blockly.LLVM.forBlock['get_datetime_component'] = function(block) { + const datetimePtr = Blockly.LLVM.valueToCode(block, 'DATETIME', Blockly.LLVM.ORDER_ATOMIC) || 'null'; + const component = block.getFieldValue('COMPONENT'); + const timezone = block.getFieldValue('TIMEZONE'); + + const timezoneGlobalName = `@.str.timezone_${timezone.replace(/[^a-zA-Z0-9_]/g, '')}`; + const timezoneStringLength = new TextEncoder().encode(timezone).length + 1; + + if (!Blockly.LLVM.generatedStrings.has(timezoneGlobalName)) { + Blockly.LLVM.globalDeclarations += `${timezoneGlobalName} = private unnamed_addr constant [${timezoneStringLength} x i8] c"${timezone}\\00", align 1\n`; + Blockly.LLVM.generatedStrings.add(timezoneGlobalName); + } + + let componentIndex = 0; + switch (component) { + case 'YEAR': componentIndex = 0; break; + case 'MONTH': componentIndex = 1; break; + case 'DAY': componentIndex = 2; break; + case 'HOUR': componentIndex = 3; break; + case 'MINUTE': componentIndex = 4; break; + case 'SECOND': componentIndex = 5; break; + } + + const resultReg = Blockly.LLVM.newReg(); + const timezoneStringPtrReg = Blockly.LLVM.newReg(); + const getComponentProcPtrReg = Blockly.LLVM.newReg(); + + let code = ` + ${timezoneStringPtrReg} = getelementptr inbounds [${timezoneStringLength} x i8], [${timezoneStringLength} x i8]* ${timezoneGlobalName}, i64 0, i64 0 + ${getComponentProcPtrReg} = load i32 (%struct.DateTimeUTC*, i32, i8*)*, i32 (%struct.DateTimeUTC*, i32, i8*)** @gGetComponentInZoneProc\n + ${resultReg} = call i32 ${getComponentProcPtrReg}(%struct.DateTimeUTC* ${datetimePtr}, i32 ${componentIndex}, i8* ${timezoneStringPtrReg}) + `; + + return [code + resultReg, Blockly.LLVM.ORDER_ATOMIC]; + }; + + const toolboxXml = ` + + + + + + 1 + + + + + + + + + + `; + + workspace = Blockly.inject('blocklyDiv', { + toolbox: toolboxXml, + zoom: { + controls: true, + wheel: true, + startScale: 1.0, + maxScale: 3, + minScale: 0.3, + scaleSpeed: 1.2 + }, + trashcan: true, + maxBlocks: 500, + scrollbars: true, + grid: { + spacing: 25, + length: 3, + colour: '#eee', + snap: true + }, + comments: true, + disable: true, + collapse: true, + readOnly: false + }); + + // --- Code-Generierungs- und Post-Funktion (angepasst, um vorher zu speichern) --- + function generateAndPostCode() { + // 1. Arbeitsbereich speichern + const state = Blockly.serialization.workspaces.save(workspace); + localStorage.setItem('blocklyWorkspace', JSON.stringify(state)); + // Optional: Konsolenmeldung zur automatischen Speicherung + // console.log("Arbeitsbereich automatisch gespeichert vor Code-Generierung."); + + // 2. Code generieren + const code = Blockly.LLVM.workspaceToCode(workspace); + + // 3. Code posten oder anzeigen + if (isWebView) { + if (window.chrome && window.chrome.webview) { + window.chrome.webview.postMessage(code); + } + } else { + document.getElementById('codeOutput').value = code; + console.log("Code in Konsole ausgegeben (nicht im WebView):"); + console.log(code); + } + } + window.generateAndPostCode = generateAndPostCode; // Exponiere für Buttons + + window.saveWorkspace = function() { + // Diese Funktion kann den Arbeitsbereich manuell speichern und einen Alert anzeigen + const state = Blockly.serialization.workspaces.save(workspace); + localStorage.setItem('blocklyWorkspace', JSON.stringify(state)); + alert('Arbeitsbereich gespeichert!'); + } + window.loadWorkspace = function() { const stateString = localStorage.getItem('blocklyWorkspace'); if (!stateString) { return; } const state = JSON.parse(stateString); Blockly.serialization.workspaces.load(state, workspace); } + + // --- Dynamische Code-Aktualisierung / Posten (ruft generateAndPostCode auf) --- + function handleCodeUpdate(event) { + if (event.isUiEvent) return; // Nur Änderungen durch Blöcke, nicht UI-Interaktionen + generateAndPostCode(); // Dies triggert jetzt sowohl Speichern als auch Generieren/Posten + } + workspace.addChangeListener(handleCodeUpdate); + loadWorkspace(); + + // --- Bedingte UI-Elemente und Splitter-Logik --- + const outputContainer = document.getElementById('outputContainer'); + const blocklyDiv = document.getElementById('blocklyDiv'); + + if (isWebView) { + // Wenn im WebView, outputContainer bleibt ausgeblendet (CSS default: display: none;) + // BlocklyDiv die gesamte Breite geben + blocklyDiv.style.width = '100%'; + // Blockly-Workspace an neue Größe anpassen, mit einer kleinen Verzögerung für WebView + setTimeout(function() { + Blockly.svgResize(workspace); + }, 100); // 100ms Verzögerung, kann bei Bedarf angepasst werden. + + // Entferne die Schaltflächen "Speichern" und "Laden", wenn im WebView + // da die Speicherung automatisch erfolgt und der Host den Code direkt bekommt. + const buttonBar = document.querySelector('.button-bar'); + if (buttonBar) { + const saveButton = buttonBar.querySelector('button[onclick="saveWorkspace()"]'); + const loadButton = buttonBar.querySelector('button[onclick="loadWorkspace()"]'); + if (saveButton) saveButton.remove(); + if (loadButton) loadButton.remove(); + // Eventuell auch den "Code generieren und posten" Button entfernen, da es automatisch passiert + // oder umbenennen in "Aktualisieren" / "Neu senden" + const postButton = buttonBar.querySelector('button[onclick="generateAndPostCode()"]'); + if (postButton) postButton.remove(); // Oder postButton.textContent = "Code senden"; + } + + } else { + // Wenn NICHT im WebView, outputContainer einblenden und Splitter-Logik initialisieren + outputContainer.style.display = 'flex'; // Einblenden als Flex-Container + + const outputArea = document.getElementById('outputArea'); + const splitter = document.getElementById('splitter'); + const container = document.getElementById('container'); + + let isDragging = false; + + splitter.addEventListener('mousedown', function(e) { + isDragging = true; + document.body.style.userSelect = 'none'; + document.body.style.cursor = 'ew-resize'; + }); + + document.addEventListener('mousemove', function(e) { + if (!isDragging) return; + + const containerRect = container.getBoundingClientRect(); + const splitterWidth = splitter.offsetWidth; + const outputAreaMarginLeft = parseInt(window.getComputedStyle(outputArea).marginLeft); + + const totalContentWidth = containerRect.width - splitterWidth - outputAreaMarginLeft; + let newBlocklyWidth = e.clientX - containerRect.left; + + const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth); + const minOutputAreaWidth = parseFloat(window.getComputedStyle(outputArea).minWidth); + + if (newBlocklyWidth < minBlocklyWidth) { + newBlocklyWidth = minBlocklyWidth; + } + + let newOutputAreaWidth = totalContentWidth - newBlocklyWidth; + + if (newOutputAreaWidth < minOutputAreaWidth) { + newOutputAreaWidth = minOutputAreaWidth; + newBlocklyWidth = totalContentWidth - newOutputAreaWidth; + if (newBlocklyWidth < minBlocklyWidth) { + newBlocklyWidth = minBlocklyWidth; + } + } + + blocklyDiv.style.width = `${newBlocklyWidth}px`; + outputArea.style.width = `${newOutputAreaWidth}px`; + + Blockly.svgResize(workspace); + }); + + document.addEventListener('mouseup', function() { + isDragging = false; + document.body.style.userSelect = ''; + document.body.style.cursor = ''; + }); + + // Initiales Resize für Nicht-WebView: Setzt die initialen Breiten + // Basierend auf der Startbreite der outputArea (500px) + const initialOutputAreaWidth = 500; // Aus CSS initialer Wert + const totalFlexContentWidth = container.offsetWidth - splitter.offsetWidth - parseInt(window.getComputedStyle(outputArea).marginLeft); + let initialBlocklyWidth = totalFlexContentWidth - initialOutputAreaWidth; + + if (initialBlocklyWidth < minBlocklyWidth) { + initialBlocklyWidth = minBlocklyWidth; + outputArea.style.width = `${totalFlexContentWidth - initialBlocklyWidth}px`; + } else { + outputArea.style.width = `${initialOutputAreaWidth}px`; + } + blocklyDiv.style.width = `${initialBlocklyWidth}px`; + + Blockly.svgResize(workspace); + } +}; diff --git a/Blockly/style.css b/Blockly/style.css new file mode 100644 index 0000000..6ff1805 --- /dev/null +++ b/Blockly/style.css @@ -0,0 +1,116 @@ +/* style.css */ + +/* Global box-sizing für konsistente Größenberechnung */ +* { + box-sizing: border-box; +} + +/* Allgemeine Styles für html und body, um den gesamten Viewport zu füllen */ +html, body { + margin: 0; + padding: 0; + height: 100%; /* Stellt sicher, dass html und body die volle Viewport-Höhe einnehmen */ + font-family: sans-serif; + overflow: hidden; /* Verhindert Scrollbalken auf der Hauptebene des Bodys */ + background-color: #F8F8F8; /* Optionale Hintergrundfarbe für den gesamten Body, falls gewünscht */ +} + +/* Body als Flex-Container, um Inhalte vertikal zu stapeln */ +body { + display: flex; + flex-direction: column; +} + +/* Optional: Style für die Überschrift im Output-Bereich und allgemeine Überschriften/Paragraphen */ +h1, h2, p { + margin: 0; + padding: 10px 0; /* Beispiel: Etwas vertikale Polsterung, keine horizontalen Ränder */ + text-align: center; /* Überschriften zentrieren */ +} + +/* Styling für die Button-Leiste */ +.button-bar { + margin-bottom: 0; /* Rand unten entfernen */ + padding: 10px 0 0 0; /* Polsterung anpassen, um oberen Rand zu entfernen */ + text-align: center; /* Buttons zentrieren */ +} + +.button-bar button { + margin: 0 5px; /* Kleiner Abstand zwischen den Buttons */ + padding: 8px 15px; + border: 1px solid #ccc; + border-radius: 4px; + background-color: #e0e0e0; + cursor: pointer; +} + +.button-bar button:hover { + background-color: #d0d0d0; +} + +/* Haupt-Container für Blockly-Workspace und Output-Bereich */ +#container { + display: flex; /* Machen Sie den Container zu einem Flex-Container, um die Kinder nebeneinander anzuordnen */ + flex-grow: 1; /* Lässt den Container den restlichen vertikalen Platz füllen */ + padding: 0; /* Polsterung um den Inhalt herum vollständig entfernen */ + box-sizing: border-box; /* Stellt sicher, dass Padding in der Größe enthalten ist */ +} + +/* Blockly-Workspace Bereich */ +#blocklyDiv { + flex-grow: 1; /* Lässt BlocklyDiv den maximalen horizontalen Platz einnehmen */ + min-width: 200px; /* Mindestbreite für Blockly, damit es nicht zu klein wird */ + height: 100%; /* Füllt die Höhe des #container */ + background-color: #FFF8E1; /* Sehr helles Beige für den Blockly-Hintergrund des Containers */ + margin: 0; /* Rand entfernen */ +} + +/* WICHTIG: Regel für den Blockly SVG-Hintergrund */ +#blocklyDiv .blocklySvg { + background-color: #FFF8E1; /* Hintergrundfarbe für das von Blockly generierte SVG-Element */ +} + +/* Optional: Sicherstellen, dass Blockly-Scrollbalken über anderen Elementen liegen */ +#blocklyDiv .blocklyScrollbarHorizontal, +#blocklyDiv .blocklyScrollbarVertical { + z-index: 100; +} + +/* Flex-Container für Splitter und Output-Bereich */ +#outputContainer { + display: none; /* Standardmäßig ausgeblendet, wird bei Bedarf von JS eingeblendet */ + flex-shrink: 0; /* Verhindert, dass dieser Container schrumpft, wenn nicht im WebView */ + flex-basis: auto; /* Initial flexible Basis */ +} + +/* Splitter-Element */ +#splitter { + width: 8px; /* Breite des Splitters */ + background-color: #ccc; /* Farbe des Splitters */ + cursor: ew-resize; /* Zeigt den "Ost-West-Größenänderung"-Cursor */ + flex-shrink: 0; /* Verhindert, dass der Splitter schrumpft */ + margin-left: 0; /* Abstand zum Blockly-Bereich entfernen */ + margin-right: 0; /* Abstand zum Output-Bereich entfernen */ +} + +/* Output-Bereich (mit Code-Textarea) */ +#outputArea { + display: flex; + flex-direction: column; /* Überschrift und Textarea vertikal stapeln */ + min-width: 200px; /* Mindestbreite für die OutputArea */ + width: 500px; /* Startbreite für den Output-Bereich, wird von JS überschrieben */ + box-sizing: border-box; + margin: 0; /* Rand entfernen */ +} + +/* Textarea für den generierten Code */ +#codeOutput { + flex-grow: 1; /* Lässt die Textarea die verbleibende Höhe in outputArea füllen */ + font-family: 'Courier New', Courier, monospace; + font-size: 14px; + white-space: pre-wrap; + border: none; /* Deaktiviert den Rand der Textarea */ + resize: none; /* Deaktiviert die eingebaute Resize-Funktion der Textarea */ + overflow: auto; /* Notwendig, damit Scrollbalken erscheinen, wenn der Inhalt die Größe überschreitet */ + padding: 10px; /* Optional: Innenabstand für den Text, damit er nicht direkt am Rand klebt */ +}