diff --git a/assets/locale/en_US.po b/assets/locale/en_US.po index d182dd0..41e2025 100644 --- a/assets/locale/en_US.po +++ b/assets/locale/en_US.po @@ -1,8 +1,9 @@ +# msgid "" msgstr "" -"Language: en_US" -"Content-Type: text/plain; charset=UTF-8" -"Plural-Forms: nplurals=2; plural=(n != 1);" +"Language: en_US\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "ui.test" msgstr "Hello this is a test." @@ -12,4 +13,3 @@ msgstr "This is a map test." msgid "test.test2" msgstr "This is another test." - diff --git a/tools/editortool/langtool.py b/tools/editortool/langtool.py index c7a23bc..ea6e108 100644 --- a/tools/editortool/langtool.py +++ b/tools/editortool/langtool.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 -from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton +from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton, QTabWidget, QFormLayout from PyQt5.QtCore import Qt import sys import os +import polib class LangToolWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Dusk Language Editor") - self.showMaximized() self.setGeometry(100, 100, 800, 600) self.current_file = None self.dirty = False @@ -19,35 +19,40 @@ class LangToolWindow(QMainWindow): central = QWidget() layout = QVBoxLayout(central) - # Header editor - header_layout = QHBoxLayout() - header_layout.addWidget(QLabel("Language:")) + tabs = QTabWidget() + # Header Tab + header_tab = QWidget() + header_layout = QFormLayout(header_tab) self.language_edit = QLineEdit() - header_layout.addWidget(self.language_edit) - header_layout.addWidget(QLabel("Plural-Forms:")) + self.language_edit.setMaximumWidth(220) + header_layout.addRow(QLabel("Language:"), self.language_edit) self.plural_edit = QLineEdit() - header_layout.addWidget(self.plural_edit) - header_layout.addWidget(QLabel("Content-Type:")) + self.plural_edit.setMaximumWidth(320) + header_layout.addRow(QLabel("Plural-Forms:"), self.plural_edit) self.content_type_edit = QLineEdit("text/plain; charset=UTF-8") - header_layout.addWidget(self.content_type_edit) - layout.addLayout(header_layout) + self.content_type_edit.setMaximumWidth(320) + header_layout.addRow(QLabel("Content-Type:"), self.content_type_edit) + tabs.addTab(header_tab, "Header") - # PO entries editor + # Strings Tab + strings_tab = QWidget() + strings_layout = QVBoxLayout(strings_tab) self.po_table = QTableWidget() self.po_table.setColumnCount(2) self.po_table.setHorizontalHeaderLabels(["msgid", "msgstr"]) self.po_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.po_table.verticalHeader().setMinimumWidth(22) self.po_table.verticalHeader().setDefaultAlignment(Qt.AlignRight | Qt.AlignVCenter) - layout.addWidget(self.po_table) - - # Row add/remove buttons + strings_layout.addWidget(self.po_table) row_btn_layout = QHBoxLayout() add_row_btn = QPushButton("Add Row") remove_row_btn = QPushButton("Remove Row") row_btn_layout.addWidget(add_row_btn) row_btn_layout.addWidget(remove_row_btn) - layout.addLayout(row_btn_layout) + strings_layout.addLayout(row_btn_layout) + tabs.addTab(strings_tab, "Strings") + + layout.addWidget(tabs) add_row_btn.clicked.connect(self.add_row) remove_row_btn.clicked.connect(self.remove_row) @@ -121,51 +126,34 @@ class LangToolWindow(QMainWindow): self.load_po_file(file_path) def load_po_file(self, file_path): - language = "" - plural = "" - entries = [] - with open(file_path, "r", encoding="utf-8") as f: - lines = f.readlines() - i = 0 - while i < len(lines): - line = lines[i].strip() - if line.startswith('"Language:'): - language = line.split(':',1)[1].strip('"\n ') - elif line.startswith('"Plural-Forms:'): - plural = line.split(':',1)[1].strip('"\n ') - elif line.startswith('msgid'): - msgid = line[6:].strip('"') - i += 1 - msgstr = lines[i].strip()[7:].strip('"') if i < len(lines) and lines[i].strip().startswith('msgstr') else "" - entries.append((msgid, msgstr)) - i += 1 + po = polib.pofile(file_path) + language = po.metadata.get('Language', '') + plural = po.metadata.get('Plural-Forms', '') + content_type = po.metadata.get('Content-Type', 'text/plain; charset=UTF-8') self.language_edit.setText(language) self.plural_edit.setText(plural) - self.po_table.setRowCount(len(entries)) - for row, (msgid, msgstr) in enumerate(entries): - self.po_table.setItem(row, 0, QTableWidgetItem(msgid)) - self.po_table.setItem(row, 1, QTableWidgetItem(msgstr)) + self.content_type_edit.setText(content_type) + self.po_table.setRowCount(len(po)) + for row, entry in enumerate(po): + self.po_table.setItem(row, 0, QTableWidgetItem(entry.msgid)) + self.po_table.setItem(row, 1, QTableWidgetItem(entry.msgstr)) def save_file(self, file_path): - language = self.language_edit.text() - plural = self.plural_edit.text() - content_type = self.content_type_edit.text() - entries = [] + po = polib.POFile() + po.metadata = { + 'Language': self.language_edit.text(), + 'Content-Type': self.content_type_edit.text(), + 'Plural-Forms': self.plural_edit.text(), + } for row in range(self.po_table.rowCount()): - msgid = self.po_table.item(row, 0) - msgstr = self.po_table.item(row, 1) - msgid_text = msgid.text() if msgid else "" - msgstr_text = msgstr.text() if msgstr else "" - if msgid_text or msgstr_text: - entries.append((msgid_text, msgstr_text)) - with open(file_path, "w", encoding="utf-8") as f: - f.write('msgid ""\nmsgstr ""\n') - f.write(f'"Language: {language}"\n') - f.write(f'"Content-Type: {content_type}"\n') - f.write(f'"Plural-Forms: {plural}"\n') - f.write('\n') - for msgid, msgstr in entries: - f.write(f'msgid "{msgid}"\nmsgstr "{msgstr}"\n\n') + msgid_item = self.po_table.item(row, 0) + msgstr_item = self.po_table.item(row, 1) + msgid = msgid_item.text() if msgid_item else '' + msgstr = msgstr_item.text() if msgstr_item else '' + if msgid or msgstr: + entry = polib.POEntry(msgid=msgid, msgstr=msgstr) + po.append(entry) + po.save(file_path) self.dirty = False self.update_save_action() @@ -197,6 +185,16 @@ class LangToolWindow(QMainWindow): self.po_table.removeRow(row) self.set_dirty() + def closeEvent(self, event): + if self.dirty: + reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before exiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) + if reply == QMessageBox.Cancel: + event.ignore() + return + elif reply == QMessageBox.Yes: + self.handle_save() + event.accept() + if __name__ == "__main__": app = QApplication(sys.argv) window = LangToolWindow() diff --git a/tools/editortool/map/glwidget.py b/tools/editortool/map/glwidget.py new file mode 100644 index 0000000..7485076 --- /dev/null +++ b/tools/editortool/map/glwidget.py @@ -0,0 +1,105 @@ +from PyQt5.QtCore import QTimer +from PyQt5.QtWidgets import QOpenGLWidget +from OpenGL.GL import * +from OpenGL.GLU import * + +class GLWidget(QOpenGLWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.xRot = 20.0 + self.yRot = 30.0 + self.zRot = 0.0 + self.rotation_speed = 2.0 + self.timer = QTimer(self) + self.timer.timeout.connect(self.updateRotation) + + def initializeGL(self): + version = glGetString(GL_VERSION) + print("GL version:", version) + glClearColor(0.1, 0.1, 0.1, 1.0) + glEnable(GL_DEPTH_TEST) + glShadeModel(GL_SMOOTH) + glEnable(GL_COLOR_MATERIAL) + glEnable(GL_LIGHTING) + glEnable(GL_LIGHT0) + light_position = [4.0, 4.0, 8.0, 1.0] + glLightfv(GL_LIGHT0, GL_POSITION, light_position) + + def resizeGL(self, w, h): + if h == 0: + h = 1 + glViewport(0, 0, w, h) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glLoadIdentity() + gluPerspective(45.0, float(w) / float(h), 0.1, 100.0) + glMatrixMode(GL_MODELVIEW) + + def paintGL(self): + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + glTranslatef(0.0, 0.0, -7.0) + glRotatef(self.xRot, 1.0, 0.0, 0.0) + glRotatef(self.yRot, 0.0, 1.0, 0.0) + glRotatef(self.zRot, 0.0, 0.0, 1.0) + self.drawCube() + + def drawCube(self): + glBegin(GL_QUADS) + # Front (red) + glColor3f(1.0, 0.0, 0.0) + glVertex3f(-1.0, -1.0, 1.0) + glVertex3f( 1.0, -1.0, 1.0) + glVertex3f( 1.0, 1.0, 1.0) + glVertex3f(-1.0, 1.0, 1.0) + # Back (green) + glColor3f(0.0, 1.0, 0.0) + glVertex3f(-1.0, -1.0, -1.0) + glVertex3f(-1.0, 1.0, -1.0) + glVertex3f( 1.0, 1.0, -1.0) + glVertex3f( 1.0, -1.0, -1.0) + # Top (blue) + glColor3f(0.0, 0.0, 1.0) + glVertex3f(-1.0, 1.0, -1.0) + glVertex3f(-1.0, 1.0, 1.0) + glVertex3f( 1.0, 1.0, 1.0) + glVertex3f( 1.0, 1.0, -1.0) + # Bottom (yellow) + glColor3f(1.0, 1.0, 0.0) + glVertex3f(-1.0, -1.0, -1.0) + glVertex3f( 1.0, -1.0, -1.0) + glVertex3f( 1.0, -1.0, 1.0) + glVertex3f(-1.0, -1.0, 1.0) + # Right (magenta) + glColor3f(1.0, 0.0, 1.0) + glVertex3f( 1.0, -1.0, -1.0) + glVertex3f( 1.0, 1.0, -1.0) + glVertex3f( 1.0, 1.0, 1.0) + glVertex3f( 1.0, -1.0, 1.0) + # Left (cyan) + glColor3f(0.0, 1.0, 1.0) + glVertex3f(-1.0, -1.0, -1.0) + glVertex3f(-1.0, -1.0, 1.0) + glVertex3f(-1.0, 1.0, 1.0) + glVertex3f(-1.0, 1.0, -1.0) + glEnd() + + def startRotation(self): + if not self.timer.isActive(): + self.timer.start(30) + + def stopRotation(self): + if self.timer.isActive(): + self.timer.stop() + + def resetView(self): + self.xRot = 20.0 + self.yRot = 30.0 + self.zRot = 0.0 + self.update() + + def updateRotation(self): + self.xRot += self.rotation_speed + self.yRot += self.rotation_speed * 0.8 + self.zRot += self.rotation_speed * 0.5 + self.update() diff --git a/tools/editortool/maptool.py b/tools/editortool/maptool.py index e7cdc5c..cca6a72 100644 --- a/tools/editortool/maptool.py +++ b/tools/editortool/maptool.py @@ -4,141 +4,16 @@ from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, - QLabel, QSlider, QOpenGLWidget, QDialog, QRadioButton, QDialogButtonBox, + QLabel, QSlider, QDialog, QRadioButton, QDialogButtonBox, QAction, QFileDialog, QLineEdit ) -from OpenGL.GL import * -from OpenGL.GLU import * - -class GLWidget(QOpenGLWidget): - def __init__(self, parent=None): - super().__init__(parent) - self.xRot = 20.0 - self.yRot = 30.0 - self.zRot = 0.0 - - self.rotation_speed = 2.0 - - self.timer = QTimer(self) - self.timer.timeout.connect(self.updateRotation) - - # ---------- OpenGL lifecycle ---------- - def initializeGL(self): - # Check that context is valid - version = glGetString(GL_VERSION) - print("GL version:", version) - - glClearColor(0.1, 0.1, 0.1, 1.0) - glEnable(GL_DEPTH_TEST) - glShadeModel(GL_SMOOTH) - - glEnable(GL_COLOR_MATERIAL) - glEnable(GL_LIGHTING) - glEnable(GL_LIGHT0) - - light_position = [4.0, 4.0, 8.0, 1.0] - glLightfv(GL_LIGHT0, GL_POSITION, light_position) - - def resizeGL(self, w, h): - if h == 0: - h = 1 - - glViewport(0, 0, w, h) - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glLoadIdentity() - gluPerspective(45.0, float(w) / float(h), 0.1, 100.0) - glMatrixMode(GL_MODELVIEW) - - def paintGL(self): - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - glTranslatef(0.0, 0.0, -7.0) - - glRotatef(self.xRot, 1.0, 0.0, 0.0) - glRotatef(self.yRot, 0.0, 1.0, 0.0) - glRotatef(self.zRot, 0.0, 0.0, 1.0) - - self.drawCube() - - # ---------- 3D object ---------- - - def drawCube(self): - glBegin(GL_QUADS) - - # Front (red) - glColor3f(1.0, 0.0, 0.0) - glVertex3f(-1.0, -1.0, 1.0) - glVertex3f( 1.0, -1.0, 1.0) - glVertex3f( 1.0, 1.0, 1.0) - glVertex3f(-1.0, 1.0, 1.0) - - # Back (green) - glColor3f(0.0, 1.0, 0.0) - glVertex3f(-1.0, -1.0, -1.0) - glVertex3f(-1.0, 1.0, -1.0) - glVertex3f( 1.0, 1.0, -1.0) - glVertex3f( 1.0, -1.0, -1.0) - - # Top (blue) - glColor3f(0.0, 0.0, 1.0) - glVertex3f(-1.0, 1.0, -1.0) - glVertex3f(-1.0, 1.0, 1.0) - glVertex3f( 1.0, 1.0, 1.0) - glVertex3f( 1.0, 1.0, -1.0) - - # Bottom (yellow) - glColor3f(1.0, 1.0, 0.0) - glVertex3f(-1.0, -1.0, -1.0) - glVertex3f( 1.0, -1.0, -1.0) - glVertex3f( 1.0, -1.0, 1.0) - glVertex3f(-1.0, -1.0, 1.0) - - # Right (magenta) - glColor3f(1.0, 0.0, 1.0) - glVertex3f( 1.0, -1.0, -1.0) - glVertex3f( 1.0, 1.0, -1.0) - glVertex3f( 1.0, 1.0, 1.0) - glVertex3f( 1.0, -1.0, 1.0) - - # Left (cyan) - glColor3f(0.0, 1.0, 1.0) - glVertex3f(-1.0, -1.0, -1.0) - glVertex3f(-1.0, -1.0, 1.0) - glVertex3f(-1.0, 1.0, 1.0) - glVertex3f(-1.0, 1.0, -1.0) - - glEnd() - - # ---------- Animation control ---------- - - def startRotation(self): - if not self.timer.isActive(): - self.timer.start(30) # ms - - def stopRotation(self): - if self.timer.isActive(): - self.timer.stop() - - def resetView(self): - self.xRot = 20.0 - self.yRot = 30.0 - self.zRot = 0.0 - self.update() - - def updateRotation(self): - self.xRot += self.rotation_speed - self.yRot += self.rotation_speed * 0.8 - self.zRot += self.rotation_speed * 0.5 - self.update() - +from editortool.map.glwidget import GLWidget class MapWindow(QMainWindow): def __init__(self): super().__init__() - self.setWindowTitle("3D Cube with GUI Buttons (QOpenGLWidget)") + self.setWindowTitle("Dusk Map Editor") self.resize(1280, 720) # File menu setup @@ -222,4 +97,4 @@ class MapWindow(QMainWindow): fname, _ = QFileDialog.getSaveFileName(self, "Save JSON File As", default_dir, "JSON Files (*.json)") if fname: self.current_file = fname - # ...save logic... + # ...save logic... \ No newline at end of file