import customtkinter as ctk
from tkinter import filedialog, messagebox
from tkinter import font as tkfont
from tkinter import ttk
import tkinter as tk
import os
import re
import json
from pathlib import Path
from datetime import datetime
import threading
import subprocess
import sys
import queue
import shutil
import platform
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("dark-blue")
GRADIENT_COLORS = {
"bg_primary": "#0D1117",
"bg_secondary": "#161B22",
"bg_tertiary": "#21262D",
"border": "#30363D",
"text_primary": "#E6EDF3",
"text_secondary": "#8B949E",
"text_tertiary": "#6E7681",
"accent_blue": "#58A6FF",
"accent_green": "#3FB950",
"accent_orange": "#D29922",
"accent_red": "#F85149",
"accent_purple": "#A371F7",
"accent_pink": "#DB61A2",
"accent_cyan": "#39D2C0",
"accent_yellow": "#E3B341",
"syntax_keyword": "#FF7B72",
"syntax_string": "#A5D6FF",
"syntax_comment": "#8B949E",
"syntax_number": "#79C0FF",
"syntax_type": "#FFA657",
"syntax_function": "#D2A8FF",
"syntax_variable": "#FFA198",
"syntax_operator": "#FF7B72",
"syntax_preprocessor": "#8B949E",
"syntax_tag": "#7EE787",
"syntax_attr": "#79C0FF",
"syntax_directive": "#D2A8FF",
"syntax_expression": "#A5D6FF",
"gutter_bg": "#0D1117",
"gutter_fg": "#484F58",
"gutter_active": "#6E7681",
"line_highlight": "#1C2128",
"selection": "#264F78",
"cursor": "#58A6FF",
"scrollbar": "#30363D",
"scrollbar_hover": "#484F58",
"toolbar_bg": "#161B22",
"statusbar_bg": "#161B22",
"tab_active_bg": "#1C2128",
"tab_inactive_bg": "#0D1117",
"dropdown_bg": "#21262D",
"dropdown_hover": "#30363D",
"menu_bg": "#161B22",
"menu_hover": "#1C2128",
"dialog_bg": "#161B22",
"button_primary": "#238636",
"button_primary_hover": "#2EA043",
"button_secondary": "#21262D",
"button_secondary_hover": "#30363D",
"button_danger": "#DA3633",
"button_danger_hover": "#F85149",
"input_bg": "#0D1117",
"input_border": "#30363D",
"input_focus": "#58A6FF",
"terminal_bg": "#0D1117",
"terminal_fg": "#E6EDF3",
"terminal_prompt": "#3FB950",
"terminal_error": "#F85149",
"terminal_warning": "#D29922",
"notification_bg": "#21262D",
"notification_info": "#58A6FF",
"notification_success": "#3FB950",
"notification_warning": "#D29922",
"notification_error": "#F85149",
}
class BuildSystem:
def __init__(self, editor):
self.editor = editor
self.output_callback = None
def check_tool(self, tool_name):
return shutil.which(tool_name) is not None
def build_python_exe(self, file_path, output_callback=None):
self.output_callback = output_callback
if not self.check_tool("nuitka") and not self.check_tool("nuitka3"):
if self.check_tool("pip") or self.check_tool("pip3"):
pip = "pip3" if self.check_tool("pip3") else "pip"
self._output("Installing Nuitka...")
subprocess.run([pip, "install", "nuitka"], capture_output=True)
else:
return False, "pip not found. Install Python first."
if not self.check_tool("nuitka") and not self.check_tool("nuitka3"):
return False, "Nuitka not found. Install: pip install nuitka"
nuitka = "nuitka3" if self.check_tool("nuitka3") else "nuitka"
output_dir = os.path.join(os.path.dirname(file_path), "build_output")
os.makedirs(output_dir, exist_ok=True)
cmd = [
nuitka,
"--standalone",
"--onefile",
"--output-dir=" + output_dir,
file_path
]
if platform.system() == "Windows":
cmd.append("--windows-icon-from-ico=NONE")
cmd.append("--mingw64")
self._output(f"🔨 Building Python exe with Nuitka...")
self._output(f"Command: {' '.join(cmd)}")
try:
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1
)
for line in process.stdout:
self._output(line.strip())
process.wait()
if process.returncode == 0:
exe_name = os.path.splitext(os.path.basename(file_path))[0]
if platform.system() == "Windows":
exe_name += ".exe"
exe_path = os.path.join(output_dir, exe_name)
if os.path.exists(exe_path):
return True, f"Build successful!\nOutput: {exe_path}"
else:
return True, f"Build completed. Check: {output_dir}"
else:
return False, f"Build failed with code {process.returncode}"
except Exception as e:
return False, str(e)
def build_cpp_exe(self, file_path, output_callback=None):
self.output_callback = output_callback
if platform.system() == "Windows":
compiler = "g++" if self.check_tool("g++") else "cl"
if compiler == "cl":
return self._build_cpp_msvc(file_path)
else:
compiler = "g++" if self.check_tool("g++") else "clang++"
if not self.check_tool(compiler):
if platform.system() == "Linux":
return False, "Install g++: sudo apt install g++"
elif platform.system() == "Darwin":
return False, "Install Xcode Command Line Tools: xcode-select --install"
else:
return False, f"Install {compiler}"
output_dir = os.path.join(os.path.dirname(file_path), "build_output")
os.makedirs(output_dir, exist_ok=True)
exe_name = os.path.splitext(os.path.basename(file_path))[0]
if platform.system() == "Windows":
exe_name += ".exe"
output_path = os.path.join(output_dir, exe_name)
cmd = [compiler, "-O3", "-std=c++17", file_path, "-o", output_path]
if platform.system() == "Windows":
cmd.extend(["-static", "-static-libgcc", "-static-libstdc++"])
self._output(f"🔨 Building C/C++ exe...")
self._output(f"Command: {' '.join(cmd)}")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode == 0:
if os.path.exists(output_path):
return True, f"Build successful!\nOutput: {output_path}"
else:
return True, f"Build completed. Check: {output_dir}"
else:
return False, f"Build failed:\n{result.stderr}"
except subprocess.TimeoutExpired:
return False, "Build timed out"
except Exception as e:
return False, str(e)
def _build_cpp_msvc(self, file_path):
self._output("🔨 Building with MSVC...")
output_dir = os.path.join(os.path.dirname(file_path), "build_output")
os.makedirs(output_dir, exist_ok=True)
exe_name = os.path.splitext(os.path.basename(file_path))[0] + ".exe"
output_path = os.path.join(output_dir, exe_name)
cmd = ["cl", "/O2", "/EHsc", f"/Fe:{output_path}", file_path]
try:
vcvars = self._find_vcvars()
if vcvars:
full_cmd = f'call "{vcvars}" && {" ".join(cmd)}'
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True, timeout=120)
else:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode == 0:
return True, f"Build successful!\nOutput: {output_path}"
else:
return False, f"Build failed:\n{result.stderr}"
except Exception as e:
return False, str(e)
def _find_vcvars(self):
paths = [
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat",
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build\\vcvars64.bat",
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Auxiliary\\Build\\vcvars64.bat",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat",
]
for path in paths:
if os.path.exists(path):
return path
return None
def build_csharp_exe(self, file_path, output_callback=None):
self.output_callback = output_callback
if not self.check_tool("dotnet"):
return False, "Install .NET SDK: https://dotnet.microsoft.com/download"
if not self.check_tool("csc"):
csc = self._find_csc()
if not csc:
return False, "csc not found. Install .NET Framework SDK or use dotnet"
else:
csc = "csc"
output_dir = os.path.join(os.path.dirname(file_path), "build_output")
os.makedirs(output_dir, exist_ok=True)
exe_name = os.path.splitext(os.path.basename(file_path))[0] + ".exe"
output_path = os.path.join(output_dir, exe_name)
cmd = [csc, "/out:" + output_path, "/optimize+", file_path]
self._output(f"🔨 Building C# exe...")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode == 0:
return True, f"Build successful!\nOutput: {output_path}"
else:
return False, f"Build failed:\n{result.stderr}"
except Exception as e:
return False, str(e)
def _find_csc(self):
paths = [
"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe",
"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe",
]
for path in paths:
if os.path.exists(path):
return path
return None
def build_rust_exe(self, file_path, output_callback=None):
self.output_callback = output_callback
if not self.check_tool("rustc"):
return False, "Install Rust: https://rustup.rs"
output_dir = os.path.join(os.path.dirname(file_path), "build_output")
os.makedirs(output_dir, exist_ok=True)
exe_name = os.path.splitext(os.path.basename(file_path))[0]
if platform.system() == "Windows":
exe_name += ".exe"
output_path = os.path.join(output_dir, exe_name)
cmd = ["rustc", "-C", "opt-level=3", file_path, "-o", output_path]
self._output(f"🔨 Building Rust exe...")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode == 0:
return True, f"Build successful!\nOutput: {output_path}"
else:
return False, f"Build failed:\n{result.stderr}"
except Exception as e:
return False, str(e)
def build_exe(self, file_path, language, output_callback=None):
if not file_path:
return False, "No file to build"
if not os.path.exists(file_path):
return False, "File not found"
builders = {
"python": self.build_python_exe,
"cpp": self.build_cpp_exe,
"c": self.build_cpp_exe,
"csharp": self.build_csharp_exe,
"cs": self.build_csharp_exe,
"rust": self.build_rust_exe,
}
if language in builders:
return builders[language](file_path, output_callback)
else:
return False, f"No exe builder for {language}"
def _output(self, text):
if self.output_callback:
self.output_callback(text)
class BuildDialog(ctk.CTkToplevel):
def __init__(self, master, editor):
super().__init__(master)
self.editor = editor
self.build_system = BuildSystem(editor)
self.title("📦 Build EXE")
self.geometry("700x550")
self.configure(fg_color=GRADIENT_COLORS["dialog_bg"])
self.resizable(True, True)
self.setup_ui()
self.auto_detect()
def setup_ui(self):
colors = GRADIENT_COLORS
title = ctk.CTkLabel(
self,
text="📦 Build EXE - One Click Compile",
font=("Segoe UI", 20, "bold"),
text_color=colors["accent_blue"]
)
title.pack(pady=15)
info_frame = ctk.CTkFrame(self, fg_color=colors["bg_secondary"], corner_radius=10)
info_frame.pack(fill="x", padx=20, pady=10)
info_grid = ctk.CTkFrame(info_frame, fg_color="transparent")
info_grid.pack(padx=15, pady=10, fill="x")
ctk.CTkLabel(info_grid, text="File:", font=("Segoe UI", 11), text_color=colors["text_secondary"]).grid(row=0, column=0, sticky="w", pady=3)
self.file_label = ctk.CTkLabel(info_grid, text="None", font=("Segoe UI", 11, "bold"), text_color=colors["text_primary"])
self.file_label.grid(row=0, column=1, sticky="w", padx=10, pady=3)
ctk.CTkLabel(info_grid, text="Language:", font=("Segoe UI", 11), text_color=colors["text_secondary"]).grid(row=1, column=0, sticky="w", pady=3)
self.lang_label = ctk.CTkLabel(info_grid, text="Unknown", font=("Segoe UI", 11, "bold"), text_color=colors["accent_green"])
self.lang_label.grid(row=1, column=1, sticky="w", padx=10, pady=3)
ctk.CTkLabel(info_grid, text="Output:", font=("Segoe UI", 11), text_color=colors["text_secondary"]).grid(row=2, column=0, sticky="w", pady=3)
self.output_label = ctk.CTkLabel(info_grid, text="build_output/", font=("Segoe UI", 11, "bold"), text_color=colors["accent_yellow"])
self.output_label.grid(row=2, column=1, sticky="w", padx=10, pady=3)
methods_frame = ctk.CTkFrame(self, fg_color=colors["bg_secondary"], corner_radius=10)
methods_frame.pack(fill="x", padx=20, pady=10)
ctk.CTkLabel(
methods_frame,
text="Build Method",
font=("Segoe UI", 13, "bold"),
text_color=colors["text_primary"]
).pack(pady=10)
self.method_var = tk.StringVar(value="auto")
methods_grid = ctk.CTkFrame(methods_frame, fg_color="transparent")
methods_grid.pack(padx=20, pady=10, fill="x")
methods = [
("auto", "🤖 Auto Detect", "Automatically choose the best method"),
("nuitka", "🐍 Nuitka (Python)", "Compile Python to standalone exe"),
("gcc", "⚡ GCC/G++ (C/C++)", "Compile C/C++ with optimizations"),
("msvc", "🪟 MSVC (C/C++/C#)", "Microsoft Visual C++ Compiler"),
("rustc", "🦀 Rustc (Rust)", "Rust compiler with release optimizations"),
]
for i, (value, title, desc) in enumerate(methods):
rb = ctk.CTkRadioButton(
methods_grid,
text="",
variable=self.method_var,
value=value,
fg_color=colors["accent_blue"],
command=self.on_method_change
)
rb.grid(row=i, column=0, pady=5)
ctk.CTkLabel(
methods_grid,
text=title,
font=("Segoe UI", 12, "bold"),
text_color=colors["text_primary"]
).grid(row=i, column=1, sticky="w", padx=10)
ctk.CTkLabel(
methods_grid,
text=desc,
font=("Segoe UI", 10),
text_color=colors["text_secondary"]
).grid(row=i, column=2, sticky="w", padx=10)
self.output_text = ctk.CTkTextbox(
self,
font=("JetBrains Mono", 10),
fg_color=colors["terminal_bg"],
text_color=colors["terminal_fg"],
height=150,
corner_radius=8
)
self.output_text.pack(fill="both", expand=True, padx=20, pady=10)
button_frame = ctk.CTkFrame(self, fg_color="transparent")
button_frame.pack(fill="x", padx=20, pady=15)
self.build_btn = ctk.CTkButton(
button_frame,
text="🔨 BUILD EXE",
width=150,
height=40,
font=("Segoe UI", 13, "bold"),
fg_color=colors["accent_green"],
hover_color=colors["button_primary_hover"],
command=self.start_build
)
self.build_btn.pack(side="left", padx=5)
open_folder_btn = ctk.CTkButton(
button_frame,
text="📂 Open Output",
width=120,
height=40,
font=("Segoe UI", 11),
fg_color=colors["button_secondary"],
hover_color=colors["button_secondary_hover"],
command=self.open_output_folder
)
open_folder_btn.pack(side="left", padx=5)
close_btn = ctk.CTkButton(
button_frame,
text="Close",
width=100,
height=40,
font=("Segoe UI", 11),
fg_color=colors["button_danger"],
hover_color=colors["button_danger_hover"],
command=self.destroy
)
close_btn.pack(side="right", padx=5)
def auto_detect(self):
if self.editor.current_file:
self.file_label.configure(text=os.path.basename(self.editor.current_file))
self.lang_label.configure(text=self.editor.lang_config.get("name", "Unknown"))
else:
self.file_label.configure(text="⚠️ No file open")
self.lang_label.configure(text="⚠️ Save file first")
def on_method_change(self):
method = self.method_var.get()
method_names = {
"auto": "Auto",
"nuitka": "Nuitka",
"gcc": "GCC",
"msvc": "MSVC",
"rustc": "Rustc"
}
self.output_text.insert("end", f"🔧 Method changed to: {method_names.get(method, method)}\n")
self.output_text.see("end")
def start_build(self):
if not self.editor.current_file:
self.output_text.insert("end", "❌ Error: No file open. Save your file first!\n")
return
if self.editor.modified:
self.output_text.insert("end", "💾 Auto-saving file...\n")
self.editor.save_file()
self.build_btn.configure(state="disabled", text="⏳ Building...")
self.output_text.delete("1.0", "end")
self.output_text.insert("end", "=" * 50 + "\n")
self.output_text.insert("end", "🔨 VERTEX BUILD SYSTEM\n")
self.output_text.insert("end", "=" * 50 + "\n\n")
self.update()
thread = threading.Thread(target=self.run_build)
thread.daemon = True
thread.start()
def run_build(self):
def output_callback(text):
self.after(0, lambda: self.output_text.insert("end", text + "\n"))
self.after(0, lambda: self.output_text.see("end"))
success, message = self.build_system.build_exe(
self.editor.current_file,
self.editor.current_language,
output_callback
)
self.after(0, lambda: self.output_text.insert("end", "\n" + "=" * 50 + "\n"))
if success:
self.after(0, lambda: self.output_text.insert("end", "✅ BUILD SUCCESSFUL!\n\n"))
self.after(0, lambda: self.output_text.insert("end", message + "\n"))
self.after(0, lambda: self.show_notification("Build Complete!", "success"))
else:
self.after(0, lambda: self.output_text.insert("end", "❌ BUILD FAILED!\n\n"))
self.after(0, lambda: self.output_text.insert("end", message + "\n"))
self.after(0, lambda: self.show_notification("Build Failed", "error"))
self.after(0, lambda: self.build_btn.configure(state="normal", text="🔨 BUILD EXE"))
def open_output_folder(self):
if self.editor.current_file:
output_dir = os.path.join(os.path.dirname(self.editor.current_file), "build_output")
if os.path.exists(output_dir):
if platform.system() == "Windows":
os.startfile(output_dir)
elif platform.system() == "Darwin":
subprocess.run(["open", output_dir])
else:
subprocess.run(["xdg-open", output_dir])
else:
self.output_text.insert("end", "⚠️ Output folder not found. Build first.\n")
def show_notification(self, message, type_="info"):
colors = {
"info": GRADIENT_COLORS["notification_info"],
"success": GRADIENT_COLORS["notification_success"],
"error": GRADIENT_COLORS["notification_error"]
}
notification = ctk.CTkLabel(
self,
text=message,
font=("Segoe UI", 12, "bold"),
text_color=colors.get(type_, GRADIENT_COLORS["text_primary"]),
fg_color=GRADIENT_COLORS["notification_bg"],
corner_radius=8
)
notification.pack(pady=5)
self.after(3000, notification.destroy)
class VertexEditor(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Vertex Editor - Build EXE Edition")
self.geometry("1400x900")
self.minsize(800, 600)
self.current_file = None
self.modified = False
self.clipboard = ""
self.font_size = 13
self.tab_size = 4
self.encoding = "utf-8"
self.current_language = "text"
self.lang_config = self.get_language_config("text")
self.tools_panel_visible = True
self.build_dialog = None
self.init_style()
self.setup_ui()
self.setup_keybindings()
self.new_file()
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.after(100, self.update_ui)
def init_style(self):
style = ttk.Style()
style.theme_use("clam")
colors = GRADIENT_COLORS
style.configure("Vertex.TFrame", background=colors["bg_primary"])
style.configure("Vertex.TLabel", background=colors["bg_primary"], foreground=colors["text_primary"])
style.configure("Vertex.TButton",
background=colors["button_secondary"],
foreground=colors["text_primary"],
borderwidth=1,
bordercolor=colors["border"],
padding=6,
relief="flat")
style.map("Vertex.TButton",
background=[("active", colors["button_secondary_hover"]),
("pressed", colors["button_primary"])])
style.configure("Vertex.Vertical.TScrollbar",
background=colors["scrollbar"],
bordercolor=colors["scrollbar"],
arrowcolor=colors["text_secondary"],
troughcolor=colors["bg_primary"])
style.map("Vertex.Vertical.TScrollbar",
background=[("active", colors["scrollbar_hover"])])
style.configure("Vertex.TNotebook",
background=colors["bg_primary"],
bordercolor=colors["border"])
style.configure("Vertex.TNotebook.Tab",
background=colors["tab_inactive_bg"],
foreground=colors["text_secondary"],
padding=[15, 5])
style.map("Vertex.TNotebook.Tab",
background=[("selected", colors["tab_active_bg"])],
foreground=[("selected", colors["text_primary"])])
style.configure("Vertex.Treeview",
background=colors["bg_secondary"],
foreground=colors["text_primary"],
fieldbackground=colors["bg_secondary"])
style.map("Vertex.Treeview",
background=[("selected", colors["selection"])])
style.configure("Vertex.TPanedwindow",
background=colors["bg_primary"],
bordercolor=colors["border"])
self.option_add("*Font", ("Segoe UI", 10))
self.option_add("*Background", colors["bg_primary"])
self.option_add("*Foreground", colors["text_primary"])
self.option_add("*selectBackground", colors["selection"])
self.option_add("*selectForeground", colors["text_primary"])
def setup_ui(self):
colors = GRADIENT_COLORS
self.main_container = ctk.CTkFrame(self, fg_color=colors["bg_primary"])
self.main_container.pack(fill="both", expand=True)
self.setup_toolbar()
self.setup_main_area()
self.setup_statusbar()
self.setup_tools_panel()
def setup_toolbar(self):
colors = GRADIENT_COLORS
self.toolbar = ctk.CTkFrame(self.main_container, height=40, fg_color=colors["toolbar_bg"], corner_radius=0)
self.toolbar.pack(fill="x", side="top")
self.toolbar_inner = ctk.CTkFrame(self.toolbar, fg_color="transparent")
self.toolbar_inner.pack(fill="x", padx=5, pady=3)
buttons = [
("📁 New", self.new_file),
("📂 Open", self.open_file_dialog),
("💾 Save", self.save_file),
("↩ Undo", self.undo),
("↪ Redo", self.redo),
("📋 Copy", self.copy),
("📌 Paste", self.paste),
("🔍 Find", self.toggle_search),
("📦 BUILD EXE", self.open_build_dialog),
("🛠️ Tools", self.toggle_tools_panel),
]
for text, command in buttons:
btn = ctk.CTkButton(
self.toolbar_inner,
text=text,
width=70 if len(text) > 3 else 32,
height=28,
fg_color="transparent" if "BUILD" not in text else colors["accent_green"],
hover_color=colors["button_secondary_hover"] if "BUILD" not in text else colors["button_primary_hover"],
text_color=colors["text_primary"],
font=("Segoe UI", 10, "bold") if "BUILD" in text else ("Segoe UI", 10),
command=command
)
btn.pack(side="left", padx=2)
def setup_main_area(self):
colors = GRADIENT_COLORS
self.main_paned = ttk.PanedWindow(self.main_container, orient="horizontal", style="Vertex.TPanedwindow")
self.main_paned.pack(fill="both", expand=True, side="top")
self.editor_container = ctk.CTkFrame(self.main_paned, fg_color=colors["bg_primary"])
self.main_paned.add(self.editor_container, weight=4)
self.editor_frame = ctk.CTkFrame(self.editor_container, fg_color=colors["bg_primary"])
self.editor_frame.pack(fill="both", expand=True)
self.line_numbers = ctk.CTkTextbox(
self.editor_frame,
width=50,
font=("JetBrains Mono", self.font_size),
fg_color=colors["gutter_bg"],
text_color=colors["gutter_fg"],
border_width=0,
corner_radius=0,
state="disabled"
)
self.line_numbers.pack(side="left", fill="y")
self.editor_scrollbar = ctk.CTkScrollbar(
self.editor_frame,
orientation="vertical",
fg_color=colors["scrollbar"],
button_color=colors["scrollbar"],
button_hover_color=colors["scrollbar_hover"]
)
self.editor_scrollbar.pack(side="right", fill="y")
self.text_editor = ctk.CTkTextbox(
self.editor_frame,
font=("JetBrains Mono", self.font_size),
fg_color=colors["bg_primary"],
text_color=colors["text_primary"],
border_width=0,
corner_radius=0,
wrap="none",
undo=True,
tabs=(self.tab_size * 10,)
)
self.text_editor.pack(side="left", fill="both", expand=True)
self.text_editor.bind("", self.on_key_press)
self.text_editor.bind("<>", self.on_text_modified)
def setup_tools_panel(self):
colors = GRADIENT_COLORS
self.tools_container = ctk.CTkFrame(self.main_paned, fg_color=colors["bg_secondary"], width=350)
self.main_paned.add(self.tools_container, weight=1)
self.tools_header = ctk.CTkFrame(self.tools_container, fg_color=colors["bg_tertiary"], height=35)
self.tools_header.pack(fill="x")
self.tools_header.pack_propagate(False)
self.tools_label = ctk.CTkLabel(
self.tools_header,
text="🛠️ TOOLBOX",
font=("Segoe UI", 11, "bold"),
text_color=colors["accent_blue"]
)
self.tools_label.pack(side="left", padx=10, pady=5)
self.tools_notebook = ttk.Notebook(self.tools_container, style="Vertex.TNotebook")
self.tools_notebook.pack(fill="both", expand=True)
self.setup_terminal_tab()
self.setup_file_explorer_tab()
self.setup_build_quick_tab()
def setup_terminal_tab(self):
colors = GRADIENT_COLORS
self.terminal_tab = ctk.CTkFrame(self.tools_notebook, fg_color=colors["terminal_bg"])
self.tools_notebook.add(self.terminal_tab, text=" 💻 Terminal ")
self.terminal_output = ctk.CTkTextbox(
self.terminal_tab,
font=("JetBrains Mono", 11),
fg_color=colors["terminal_bg"],
text_color=colors["terminal_fg"],
border_width=0,
corner_radius=0,
wrap="word"
)
self.terminal_output.pack(fill="both", expand=True, padx=5, pady=5)
self.terminal_input_frame = ctk.CTkFrame(self.terminal_tab, fg_color=colors["bg_tertiary"], height=30)
self.terminal_input_frame.pack(fill="x", side="bottom")
ctk.CTkLabel(
self.terminal_input_frame,
text="$",
font=("JetBrains Mono", 11, "bold"),
text_color=colors["terminal_prompt"]
).pack(side="left", padx=(5, 0))
self.terminal_input = ctk.CTkEntry(
self.terminal_input_frame,
font=("JetBrains Mono", 11),
fg_color=colors["terminal_bg"],
text_color=colors["terminal_fg"],
border_width=0,
corner_radius=0
)
self.terminal_input.pack(side="left", fill="x", expand=True, padx=5, pady=3)
self.terminal_input.bind("", self.execute_terminal_command)
self.terminal_output.insert("end", "Vertex Terminal v1.0\n")
self.terminal_output.insert("end", f"{'─' * 40}\n")
self.terminal_output.insert("end", "$ ")
def execute_terminal_command(self, event=None):
command = self.terminal_input.get().strip()
if not command:
return
self.terminal_input.delete(0, "end")
self.terminal_output.insert("end", f"{command}\n")
if command in ["clear", "cls"]:
self.terminal_output.delete("1.0", "end")
self.terminal_output.insert("end", "$ ")
return
try:
if platform.system() == "Windows":
process = subprocess.Popen(["cmd", "/c", command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
else:
process = subprocess.Popen(["/bin/sh", "-c", command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
stdout, stderr = process.communicate(timeout=30)
if stdout:
self.terminal_output.insert("end", stdout)
if stderr:
self.terminal_output.insert("end", stderr)
except subprocess.TimeoutExpired:
process.kill()
self.terminal_output.insert("end", "[Timeout]\n")
except Exception as e:
self.terminal_output.insert("end", f"[Error] {e}\n")
self.terminal_output.insert("end", "$ ")
self.terminal_output.see("end")
def setup_file_explorer_tab(self):
colors = GRADIENT_COLORS
self.explorer_tab = ctk.CTkFrame(self.tools_notebook, fg_color=colors["bg_secondary"])
self.tools_notebook.add(self.explorer_tab, text=" 📁 Files ")
self.explorer_path_var = ctk.StringVar(value=os.path.expanduser("~"))
self.explorer_path = ctk.CTkEntry(
self.explorer_tab,
textvariable=self.explorer_path_var,
font=("Segoe UI", 10),
fg_color=colors["input_bg"],
text_color=colors["text_primary"],
border_width=0
)
self.explorer_path.pack(fill="x", padx=5, pady=5)
self.explorer_path.bind("", self.refresh_file_explorer)
self.explorer_tree = ttk.Treeview(self.explorer_tab, style="Vertex.Treeview", show="tree")
self.explorer_tree.pack(fill="both", expand=True)
self.explorer_tree.bind("", self.on_explorer_double_click)
self.refresh_file_explorer()
def refresh_file_explorer(self, event=None):
self.explorer_tree.delete(*self.explorer_tree.get_children())
path = self.explorer_path_var.get()
try:
items = os.listdir(path)
dirs = sorted([d for d in items if os.path.isdir(os.path.join(path, d))])
files = sorted([f for f in items if os.path.isfile(os.path.join(path, f))])
for d in dirs:
self.explorer_tree.insert("", "end", text=f"📁 {d}", values=(os.path.join(path, d),))
for f in files:
self.explorer_tree.insert("", "end", text=f"📄 {f}", values=(os.path.join(path, f),))
except PermissionError:
pass
def on_explorer_double_click(self, event):
selection = self.explorer_tree.selection()
if selection:
path = self.explorer_tree.item(selection[0], "values")[0]
if os.path.isfile(path):
self.open_file(path)
elif os.path.isdir(path):
self.explorer_path_var.set(path)
self.refresh_file_explorer()
def setup_build_quick_tab(self):
colors = GRADIENT_COLORS
self.build_quick_tab = ctk.CTkFrame(self.tools_notebook, fg_color=colors["bg_secondary"])
self.tools_notebook.add(self.build_quick_tab, text=" 📦 Build ")
ctk.CTkLabel(
self.build_quick_tab,
text="Quick Build",
font=("Segoe UI", 14, "bold"),
text_color=colors["accent_green"]
).pack(pady=15)
self.build_info = ctk.CTkTextbox(
self.build_quick_tab,
font=("JetBrains Mono", 10),
fg_color=colors["bg_primary"],
text_color=colors["text_primary"],
height=100,
corner_radius=8
)
self.build_info.pack(fill="x", padx=15, pady=10)
self.build_info.insert("end", "Ready to build!\n")
self.build_info.insert("end", "Click BUILD EXE in toolbar\n")
self.build_info.insert("end", "or press Ctrl+Shift+B\n")
build_btn = ctk.CTkButton(
self.build_quick_tab,
text="🔨 OPEN BUILD DIALOG",
height=45,
font=("Segoe UI", 12, "bold"),
fg_color=colors["accent_green"],
hover_color=colors["button_primary_hover"],
command=self.open_build_dialog
)
build_btn.pack(fill="x", padx=20, pady=10)
quick_build_btn = ctk.CTkButton(
self.build_quick_tab,
text="⚡ QUICK BUILD (Auto)",
height=35,
font=("Segoe UI", 11),
fg_color=colors["button_secondary"],
hover_color=colors["button_secondary_hover"],
command=self.quick_build
)
quick_build_btn.pack(fill="x", padx=20, pady=5)
def setup_statusbar(self):
colors = GRADIENT_COLORS
self.statusbar = ctk.CTkFrame(self.main_container, height=25, fg_color=colors["statusbar_bg"], corner_radius=0)
self.statusbar.pack(fill="x", side="bottom")
ctk.CTkLabel(self.statusbar, text=" Ready", font=("Segoe UI", 9), text_color=colors["text_secondary"]).pack(side="left")
status_right = ctk.CTkFrame(self.statusbar, fg_color="transparent")
status_right.pack(side="right")
self.line_col_label = ctk.CTkLabel(status_right, text="Ln 1, Col 1 ", font=("Segoe UI", 9), text_color=colors["text_secondary"])
self.line_col_label.pack(side="left")
self.language_label = ctk.CTkLabel(status_right, text="Text ", font=("Segoe UI", 9), text_color=colors["text_secondary"])
self.language_label.pack(side="left")
def setup_keybindings(self):
self.bind("", lambda e: self.new_file())
self.bind("", lambda e: self.open_file_dialog())
self.bind("", lambda e: self.save_file())
self.bind("", lambda e: self.on_close())
self.bind("", lambda e: self.undo())
self.bind("", lambda e: self.redo())
self.bind("", lambda e: self.cut())
self.bind("", lambda e: self.copy())
self.bind("", lambda e: self.paste())
self.bind("", lambda e: self.toggle_search())
self.bind("", lambda e: self.toggle_tools_panel())
self.bind("", lambda e: self.open_build_dialog())
self.bind("", lambda e: self.terminal_input.focus_set())
def get_language_config(self, lang):
configs = {
"python": {"name": "Python", "keywords": ["False","None","True","and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","nonlocal","not","or","pass","raise","return","try","while","with","yield"], "types": ["int","float","str","list","dict","tuple","set","bool","bytes","object"]},
"cpp": {"name": "C++", "keywords": ["auto","break","case","class","const","continue","default","delete","do","else","enum","extern","for","friend","goto","if","inline","namespace","new","operator","private","protected","public","return","sizeof","static","struct","switch","template","this","throw","try","typedef","union","using","virtual","void","volatile","while"], "types": ["bool","char","double","float","int","long","short","string","vector","map"]},
"c": {"name": "C", "keywords": ["auto","break","case","char","const","continue","default","do","double","else","enum","extern","float","for","goto","if","int","long","register","return","short","signed","sizeof","static","struct","switch","typedef","union","unsigned","void","volatile","while"], "types": ["int","char","float","double","long","short","unsigned","void"]},
"rust": {"name": "Rust", "keywords": ["as","break","const","continue","crate","else","enum","extern","false","fn","for","if","impl","in","let","loop","match","mod","move","mut","pub","ref","return","self","Self","static","struct","super","trait","true","type","unsafe","use","where","while","async","await"], "types": ["i32","i64","f32","f64","bool","char","str","String","Vec","Option","Result"]},
"csharp": {"name": "C#", "keywords": ["abstract","as","base","bool","break","byte","case","catch","char","checked","class","const","continue","decimal","default","delegate","do","double","else","enum","event","explicit","extern","false","finally","fixed","float","for","foreach","goto","if","implicit","in","int","interface","internal","is","lock","long","namespace","new","null","object","operator","out","override","params","private","protected","public","readonly","ref","return","sbyte","sealed","short","sizeof","stackalloc","static","string","struct","switch","this","throw","true","try","typeof","uint","ulong","unchecked","unsafe","ushort","using","virtual","void","volatile","while"], "types": ["string","int","long","float","double","bool","decimal","char","byte","object","dynamic","var"]},
}
return configs.get(lang, {"name": "Text", "keywords": [], "types": []})
def new_file(self, event=None):
if self.modified:
response = messagebox.askyesnocancel("Save", "Save current file?")
if response is None: return
if response: self.save_file()
self.text_editor.delete("1.0", "end")
self.current_file = None
self.modified = False
self.current_language = "text"
self.lang_config = self.get_language_config("text")
self.update_title()
self.update_statusbar()
self.update_line_numbers()
def open_file_dialog(self):
file_path = filedialog.askopenfilename(title="Open File", filetypes=[
("All Files", "*.*"),
("Python", "*.py"),
("C/C++", "*.cpp *.c *.h *.hpp"),
("C#", "*.cs"),
("Rust", "*.rs"),
("Java", "*.java"),
("JavaScript", "*.js"),
("HTML", "*.html *.htm"),
("XML", "*.xml"),
("JSON", "*.json"),
("Text", "*.txt"),
])
if file_path:
self.open_file(file_path)
def open_file(self, file_path):
try:
with open(file_path, "r", encoding=self.encoding) as f:
content = f.read()
self.text_editor.delete("1.0", "end")
self.text_editor.insert("1.0", content)
self.current_file = file_path
self.modified = False
ext = Path(file_path).suffix.lower()
lang_map = {
".py": "python", ".cpp": "cpp", ".cc": "cpp", ".cxx": "cpp",
".c": "c", ".h": "cpp", ".hpp": "cpp",
".cs": "csharp", ".rs": "rust",
".java": "java", ".js": "javascript",
".ts": "typescript", ".html": "html", ".xml": "xml",
}
self.current_language = lang_map.get(ext, "text")
self.lang_config = self.get_language_config(self.current_language)
self.update_title()
self.update_statusbar()
self.update_line_numbers()
except Exception as e:
messagebox.showerror("Error", f"Failed to open file: {str(e)}")
def save_file(self, event=None):
if self.current_file:
try:
content = self.text_editor.get("1.0", "end-1c")
with open(self.current_file, "w", encoding=self.encoding) as f:
f.write(content)
self.modified = False
self.update_title()
return True
except Exception as e:
messagebox.showerror("Error", f"Failed to save: {str(e)}")
else:
return self.save_file_as()
return False
def save_file_as(self):
file_path = filedialog.asksaveasfilename(title="Save As", defaultextension=".txt")
if file_path:
self.current_file = file_path
return self.save_file()
return False
def cut(self, event=None):
try:
self.clipboard = self.text_editor.get("sel.first", "sel.last")
self.text_editor.delete("sel.first", "sel.last")
self.modified = True
except: pass
def copy(self, event=None):
try: self.clipboard = self.text_editor.get("sel.first", "sel.last")
except: pass
def paste(self, event=None):
if self.clipboard:
try: self.text_editor.delete("sel.first", "sel.last")
except: pass
self.text_editor.insert("insert", self.clipboard)
self.modified = True
def undo(self, event=None):
try: self.text_editor.edit_undo(); self.modified = True
except: pass
def redo(self, event=None):
try: self.text_editor.edit_redo(); self.modified = True
except: pass
def toggle_search(self, event=None):
dialog = ctk.CTkInputDialog(text="Search:", title="Find", fg_color=GRADIENT_COLORS["dialog_bg"])
search_text = dialog.get_input()
if search_text:
pos = self.text_editor.search(search_text, "1.0", stopindex="end")
if pos:
end = f"{pos}+{len(search_text)}c"
self.text_editor.tag_add("sel", pos, end)
self.text_editor.see(pos)
def toggle_tools_panel(self, event=None):
if self.tools_panel_visible:
self.tools_container.pack_forget()
else:
self.tools_container.pack(side="right", fill="y")
self.tools_panel_visible = not self.tools_panel_visible
def open_build_dialog(self, event=None):
if self.build_dialog is None or not self.build_dialog.winfo_exists():
self.build_dialog = BuildDialog(self, self)
self.build_dialog.focus()
def quick_build(self):
if not self.current_file:
messagebox.showwarning("No File", "Open a file first!")
return
if self.modified:
self.save_file()
self.open_build_dialog()
if self.build_dialog:
self.build_dialog.start_build()
def on_key_press(self, event):
self.update_line_numbers()
self.update_statusbar()
def on_text_modified(self, event):
if self.text_editor.edit_modified():
self.modified = True
self.text_editor.edit_modified(False)
self.update_title()
def update_ui(self):
self.after(100, self.update_ui)
def update_title(self):
title = f"Vertex Editor - {os.path.basename(self.current_file) if self.current_file else 'Untitled'}"
if self.modified: title += " ●"
self.title(title)
def update_statusbar(self):
pos = self.text_editor.index("insert")
line, col = pos.split(".")
self.line_col_label.configure(text=f"Ln {line}, Col {int(col) + 1} ")
self.language_label.configure(text=f"{self.lang_config.get('name', 'Text')} ")
def update_line_numbers(self):
lines = int(self.text_editor.index("end-1c").split(".")[0])
line_numbers = "\n".join(str(i) for i in range(1, lines + 1))
self.line_numbers.configure(state="normal")
self.line_numbers.delete("1.0", "end")
self.line_numbers.insert("1.0", line_numbers)
self.line_numbers.configure(state="disabled")
def on_close(self):
if self.modified:
response = messagebox.askyesnocancel("Save", "Save changes before closing?")
if response is None: return
if response: self.save_file()
self.destroy()
if __name__ == "__main__":
app = VertexEditor()
app.mainloop()