diff --git a/classes/content.py b/classes/content.py index 7be6bed..eb7b9e2 100644 --- a/classes/content.py +++ b/classes/content.py @@ -1,16 +1,231 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +import sys, os + from PyQt5 import QtCore from PyQt5.QtGui import QFont, QSyntaxHighlighter -from PyQt5.QtWidgets import QWidget, QLabel, QTabWidget, QHBoxLayout, QSplitter, QPlainTextEdit +from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QListView, QAbstractItemView + +import markdown +import re +import json -class Summary(QLabel): - def __init__(self): +class Summary(QWidget): + def __init__(self, core): super(Summary, self).__init__() + self.core = core + + sum_json = open(os.path.join(self.core.cwd,'.config/summary.json')).read() + self.sum = json.loads(sum_json) + + vbox = QVBoxLayout() + vbox.setContentsMargins(0,0,0,0) + + self.list = SummaryList(self) + vbox.addWidget(self.list) + + # self.actions = SummaryActions(self) + # self.addWidget(self.actions) + + self.setLayout(vbox) + +class SumListModel(QtCore.QAbstractListModel): + dragDropFinished = QtCore.pyqtSignal() + def __init__(self,sum): + QtCore.QAbstractItemModel.__init__(self) + self.nodes = ['node0', 'node1', 'node2', 'node3', 'node4', 'node5'] + self.lastDroppedItems = [] + self.pendingRemoveRowsAfterDrop = False + + def rowForItem(self, text): + ''' + rowForItem method returns the row corresponding to the passed in item + or None if no such item exists in the model + ''' + try: + row = self.nodes.index(text) + except ValueError: + return None + return row + + def index(self, row, column, parent): + if row < 0 or row >= len(self.nodes): + return QtCore.QModelIndex() + return self.createIndex(row, column) + + def parent(self, index): + return QtCore.QModelIndex() + + def rowCount(self, index): + if index.isValid(): + return 0 + return len(self.nodes) + + def data(self, index, role): + if not index.isValid(): + return None + if role == QtCore.Qt.DisplayRole: + row = index.row() + if row < 0 or row >= len(self.nodes): + return None + return self.nodes[row] + else: + return None + + def supportedDropActions(self): + return QtCore.Qt.MoveAction + + def flags(self, index): + if not index.isValid(): + return QtCore.Qt.ItemIsEnabled + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \ + QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled + + def insertRows(self, row, count, index): + if index.isValid(): + return False + if count <= 0: + return False + # inserting 'count' empty rows starting at 'row' + self.beginInsertRows(QtCore.QModelIndex(), row, row + count - 1) + for i in range(0, count): + self.nodes.insert(row + i, '') + self.endInsertRows() + return True + + def removeRows(self, row, count, index): + if index.isValid(): + return False + if count <= 0: + return False + num_rows = self.rowCount(QtCore.QModelIndex()) + self.beginRemoveRows(QtCore.QModelIndex(), row, row + count - 1) + for i in range(count, 0, -1): + self.nodes.pop(row - i + 1) + self.endRemoveRows() + + if self.pendingRemoveRowsAfterDrop: + ''' + If we got here, it means this call to removeRows is the automatic + 'cleanup' action after drag-n-drop performed by Qt + ''' + self.pendingRemoveRowsAfterDrop = False + self.dragDropFinished.emit() + + return True + + def setData(self, index, value, role): + if not index.isValid(): + return False + if index.row() < 0 or index.row() > len(self.nodes): + return False + self.nodes[index.row()] = str(value) + self.dataChanged.emit(index, index) + return True + + def mimeTypes(self): + return ['application/vnd.treeviewdragdrop.list'] + + def mimeData(self, indexes): + mimedata = QtCore.QMimeData() + encoded_data = QtCore.QByteArray() + stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.WriteOnly) + for index in indexes: + if index.isValid(): + text = self.data(index, 0) + stream << QtCore.QByteArray(text.encode('utf-8')) + mimedata.setData('application/vnd.treeviewdragdrop.list', encoded_data) + return mimedata + + def dropMimeData(self, data, action, row, column, parent): + if action == QtCore.Qt.IgnoreAction: + return True + if not data.hasFormat('application/vnd.treeviewdragdrop.list'): + return False + if column > 0: + return False + + num_rows = self.rowCount(QtCore.QModelIndex()) + if num_rows <= 0: + return False + + if row < 0: + if parent.isValid(): + row = parent.row() + else: + return False + + encoded_data = data.data('application/vnd.treeviewdragdrop.list') + stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly) + + new_items = [] + rows = 0 + while not stream.atEnd(): + text = QtCore.QByteArray() + stream >> text + text = bytes(text).decode('utf-8') + index = self.nodes.index(text) + new_items.append((text, index)) + rows += 1 + + self.lastDroppedItems = [] + for (text, index) in new_items: + target_row = row + if index < row: + target_row += 1 + self.beginInsertRows(QtCore.QModelIndex(), target_row, target_row) + self.nodes.insert(target_row, text) + self.endInsertRows() + self.lastDroppedItems.append(text) + row += 1 + + self.pendingRemoveRowsAfterDrop = True + return True + +class SumListSelectionModel(QtCore.QItemSelectionModel): + def __init__(self, parent=None): + QtCore.QItemSelectionModel.__init__(self, parent) + + def onModelItemsReordered(self): + new_selection = QtCore.QItemSelection() + new_index = QtCore.QModelIndex() + for item in self.model().lastDroppedItems: + row = self.model().rowForItem(item) + if row is None: + continue + new_index = self.model().index(row, 0, QtCore.QModelIndex()) + new_selection.select(new_index, new_index) + + self.clearSelection() + flags = QtCore.QItemSelectionModel.ClearAndSelect | \ + QtCore.QItemSelectionModel.Rows | \ + QtCore.QItemSelectionModel.Current + self.select(new_selection, flags) + self.setCurrentIndex(new_index, flags) + +class SummaryList(QListView): + def __init__(self,prt): + super(SummaryList, self).__init__() + print("list") + self.prt = prt + model = SumListModel(sum) + selectionModel = SumListSelectionModel(model) + model.dragDropFinished.connect(selectionModel.onModelItemsReordered) + self.setModel(model) + self.setSelectionModel(selectionModel) + self.setDragDropMode(QAbstractItemView.InternalMove) + self.setDragDropOverwriteMode(False) +class SummaryActions(QWidget): + def __init__(self,sum): + print("actions") + # for p in self.sum: + +# class SummaryActions(): +# def class ContentStack(QWidget): def __init__(self, core): @@ -23,7 +238,7 @@ class ContentStack(QWidget): hsplitter = QSplitter(QtCore.Qt.Horizontal) - self.summary = QLabel("Summary (markdown files list).") + self.summary = Summary(core) hsplitter.addWidget(self.summary) self.mdsource = QLabel("Content (markdown src).") diff --git a/classes/design.py b/classes/design.py new file mode 100644 index 0000000..c47b973 --- /dev/null +++ b/classes/design.py @@ -0,0 +1,125 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import sys, os, re + +from PyQt5 import QtCore +from PyQt5.QtCore import QUrl +from PyQt5.QtGui import QKeySequence, QFont, QSyntaxHighlighter +from PyQt5.QtWidgets import QWidget, QLabel, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut +from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWebKitWidgets import QWebView, QWebInspector + + +class WebkitView(QWebView): + def __init__(self, port): + self.port = port + self.view = QWebView.__init__(self) + self.load(QUrl('http://localhost:'+str(self.port))) + self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) + # self.settings().setAttribute(QWebSettings.PluginsEnabled, True) + + +class WebkitInspector(QWebInspector): + def __init__(self, webkitview): + super(WebkitInspector, self).__init__() + self.webkitview = webkitview + self.setPage(self.webkitview.page()) + # TODO: webkitinspector is disappearing when chaging tabs + +class CodeEditor(QPlainTextEdit): + def __init__(self, core, tabs, file=None): + super(CodeEditor, self).__init__() + self.core = core + self.tabs = tabs + # self.file = file + self.file = os.path.join(self.core.cwd,file) + + self.insertPlainText(open(self.file, 'r').read()) + self.changed = False + self.textChanged.connect(self.onTextChanged) + + self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self) + self.shortcut.activated.connect(self.save) + + def onTextChanged(self): + # print('textChanged') + # print(self.toPlainText()) + # open(self.file, 'w').write(self.toPlainText()) + if not self.changed: + self.changed = True + i = self.tabs.currentIndex() + self.tabs.setTabText(i, "* "+self.tabs.tabText(i)) + + def save(self): + if self.changed: + open(self.file, 'w').write(self.toPlainText()) + i = self.tabs.currentIndex() + self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i))) + self.changed = False + # TODO: how to combine file save and project save + + +class Editor(QWidget): + def __init__(self, core): + super(Editor, self).__init__() + self.core = core + + self.layout = QVBoxLayout(self) + self.layout.setContentsMargins(0,0,0,0) + + # Initialize tab screen + self.tabs = QTabWidget() + + self.scsstab = CodeEditor(core, self.tabs, 'assets/scss/styles.scss') + self.jstab = CodeEditor(core, self.tabs, 'assets/js/script.js') + + # Add tabs + self.tabs.addTab(self.scsstab,"scss") + self.tabs.addTab(self.jstab,"js") + + # Add tabs to widget + self.layout.addWidget(self.tabs) + self.setLayout(self.layout) + + # font = QFont() + # font.setFamily('Courier') + # font.setFixedPitch(True) + # font.setPointSize(10) + # self.setFont(font) + # self.highlighter = Highlighter(self.document()) + # https://pypi.python.org/pypi/QScintilla/2.9.2 + + +class DesignStack(QWidget): + def __init__(self, core): + super(DesignStack, self).__init__() + + # self.grid = QGridLayout() + hbox = QHBoxLayout() + hbox.setContentsMargins(0,0,0,0) + self.setLayout(hbox) + + + # webviewbox = QVBoxLayout() + vsplitter = QSplitter(QtCore.Qt.Vertical) + + self.webkitview = WebkitView(core.server.port) + vsplitter.addWidget(self.webkitview) + + self.webkitinspector = WebkitInspector(self.webkitview) + vsplitter.addWidget(self.webkitinspector) + + hsplitter = QSplitter(QtCore.Qt.Horizontal) + hsplitter.addWidget(vsplitter) + + self.editor = Editor(core) + hsplitter.addWidget(self.editor) + + hbox.addWidget(hsplitter) + + + # def onChanged(self, text): + # print("ViewTba Layout Changed") + # self.lbl.setText(text) + # self.lbl.adjustSize()