diff --git a/MarkViewDesktop/Combinear.qss b/MarkViewDesktop/Combinear.qss new file mode 100644 index 0000000..03e7059 --- /dev/null +++ b/MarkViewDesktop/Combinear.qss @@ -0,0 +1,987 @@ +/*Copyright (c) DevSec Studio. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/*-----QWidget-----*/ +QWidget +{ + background-color: #3a3a3a; + color: #fff; + selection-background-color: #b78620; + selection-color: #000; + +} + + +/*-----QLabel-----*/ +QLabel +{ + background-color: transparent; + color: #fff; + +} + + +/*-----QMenuBar-----*/ +QMenuBar +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(57, 57, 57, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #000; + color: #fff; + +} + + +QMenuBar::item +{ + background-color: transparent; + +} + + +QMenuBar::item:selected +{ + background-color: rgba(183, 134, 32, 20%); + border: 1px solid #b78620; + color: #fff; + +} + + +QMenuBar::item:pressed +{ + background-color: rgb(183, 134, 32); + border: 1px solid #b78620; + color: #fff; + +} + + +/*-----QMenu-----*/ +QMenu +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(57, 57, 57, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #222; + padding: 4px; + color: #fff; + +} + + +QMenu::item +{ + background-color: transparent; + padding: 2px 20px 2px 20px; + +} + + +QMenu::separator +{ + background-color: rgb(183, 134, 32); + height: 1px; + +} + + +QMenu::item:disabled +{ + color: #555; + background-color: transparent; + padding: 2px 20px 2px 20px; + +} + + +QMenu::item:selected +{ + background-color: rgba(183, 134, 32, 20%); + border: 1px solid #b78620; + color: #fff; + +} + + +/*-----QToolBar-----*/ +QToolBar +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(69, 69, 69, 255),stop:1 rgba(58, 58, 58, 255)); + border-top: none; + border-bottom: 1px solid #4f4f4f; + border-left: 1px solid #4f4f4f; + border-right: 1px solid #4f4f4f; + +} + + +QToolBar::separator +{ + background-color: #2e2e2e; + width: 1px; + +} + + +/*-----QToolButton-----*/ +QToolButton +{ + background-color: transparent; + color: #fff; + padding: 5px; + padding-left: 8px; + padding-right: 8px; + margin-left: 1px; +} + + +QToolButton:hover +{ + background-color: rgba(183, 134, 32, 20%); + border: 1px solid #b78620; + color: #fff; + +} + + +QToolButton:pressed +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(57, 57, 57, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #b78620; + +} + + +QToolButton:checked +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(57, 57, 57, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #222; +} + + +/*-----QPushButton-----*/ +QPushButton +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(84, 84, 84, 255),stop:1 rgba(59, 59, 59, 255)); + color: #ffffff; + min-width: 80px; + border-style: solid; + border-width: 1px; + border-radius: 3px; + border-color: #051a39; + padding: 5px; + +} + + +QPushButton::flat +{ + background-color: transparent; + border: none; + color: #fff; + +} + + +QPushButton::disabled +{ + background-color: #404040; + color: #656565; + border-color: #051a39; + +} + + +QPushButton::hover +{ + background-color: rgba(183, 134, 32, 20%); + border: 1px solid #b78620; + +} + + +QPushButton::pressed +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(74, 74, 74, 255),stop:1 rgba(49, 49, 49, 255)); + border: 1px solid #b78620; + +} + + +QPushButton::checked +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(74, 74, 74, 255),stop:1 rgba(49, 49, 49, 255)); + border: 1px solid #222; + +} + + +/*-----QLineEdit-----*/ +QLineEdit +{ + background-color: #131313; + color : #eee; + border: 1px solid #343434; + border-radius: 2px; + padding: 3px; + padding-left: 5px; + +} + + +/*-----QPlainTExtEdit-----*/ +QPlainTextEdit +{ + background-color: #131313; + color : #eee; + border: 1px solid #343434; + border-radius: 2px; + padding: 3px; + padding-left: 5px; + +} + + +/*-----QTabBar-----*/ +QTabBar::tab +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(84, 84, 84, 255),stop:1 rgba(59, 59, 59, 255)); + color: #ffffff; + border-style: solid; + border-width: 1px; + border-color: #666; + border-bottom: none; + padding: 5px; + padding-left: 15px; + padding-right: 15px; + +} + + +QTabWidget::pane +{ + background-color: red; + border: 1px solid #666; + top: 1px; + +} + + +QTabBar::tab:last +{ + margin-right: 0; + +} + + +QTabBar::tab:first:!selected +{ + background-color: #0c0c0d; + margin-left: 0px; + +} + + +QTabBar::tab:!selected +{ + color: #b1b1b1; + border-bottom-style: solid; + background-color: #0c0c0d; + +} + + +QTabBar::tab:selected +{ + margin-bottom: 0px; + +} + + +QTabBar::tab:!selected:hover +{ + border-top-color: #b78620; + +} + + +/*-----QComboBox-----*/ +QComboBox +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(84, 84, 84, 255),stop:1 rgba(59, 59, 59, 255)); + border: 1px solid #000; + padding-left: 6px; + color: #ffffff; + height: 20px; + +} + + +QComboBox::disabled +{ + background-color: #404040; + color: #656565; + border-color: #051a39; + +} + + +QComboBox:on +{ + background-color: #b78620; + color: #000; + +} + + +QComboBox QAbstractItemView +{ + background-color: #383838; + color: #ffffff; + border: 1px solid black; + selection-background-color: #b78620; + outline: 0; + +} + + +QComboBox::drop-down +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(57, 57, 57, 255),stop:1 rgba(50, 50, 50, 255)); + subcontrol-origin: padding; + subcontrol-position: top right; + width: 15px; + border-left-width: 1px; + border-left-color: black; + border-left-style: solid; + +} + + +QComboBox::down-arrow +{ + image: url(://arrow-down.png); + width: 8px; + height: 8px; +} + + +/*-----QSpinBox & QDateTimeEdit-----*/ +QSpinBox, +QDateTimeEdit +{ + background-color: #131313; + color : #eee; + border: 1px solid #343434; + padding: 3px; + padding-left: 5px; + border-radius : 2px; + +} + + +QSpinBox::up-button, +QDateTimeEdit::up-button +{ + border-top-right-radius:2px; + background-color: #777777; + width: 16px; + border-width: 1px; + +} + + +QSpinBox::up-button:hover, +QDateTimeEdit::up-button:hover +{ + background-color: #585858; + +} + + +QSpinBox::up-button:pressed, +QDateTimeEdit::up-button:pressed +{ + background-color: #252525; + width: 16px; + border-width: 1px; + +} + + +QSpinBox::up-arrow, +QDateTimeEdit::up-arrow +{ + image: url(://arrow-up.png); + width: 7px; + height: 7px; + +} + + +QSpinBox::down-button, +QDateTimeEdit::down-button +{ + border-bottom-right-radius:2px; + background-color: #777777; + width: 16px; + border-width: 1px; + +} + + +QSpinBox::down-button:hover, +QDateTimeEdit::down-button:hover +{ + background-color: #585858; + +} + + +QSpinBox::down-button:pressed, +QDateTimeEdit::down-button:pressed +{ + background-color: #252525; + width: 16px; + border-width: 1px; + +} + + +QSpinBox::down-arrow, +QDateTimeEdit::down-arrow +{ + image: url(://arrow-down.png); + width: 7px; + height: 7px; + +} + + +/*-----QGroupBox-----*/ +QGroupBox +{ + border: 1px solid; + border-color: #666666; + border-radius: 5px; + margin-top: 20px; + +} + + +QGroupBox::title +{ + background-color: transparent; + color: #eee; + subcontrol-origin: margin; + padding: 5px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + +} + + +/*-----QHeaderView-----*/ +QHeaderView::section +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(60, 60, 60, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #000; + color: #fff; + text-align: left; + padding: 4px; + +} + + +QHeaderView::section:disabled +{ + background-color: #525251; + color: #656565; + +} + + +QHeaderView::section:checked +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(60, 60, 60, 255),stop:1 rgba(50, 50, 50, 255)); + color: #fff; + +} + + +QHeaderView::section::vertical::first, +QHeaderView::section::vertical::only-one +{ + border-top: 1px solid #353635; + +} + + +QHeaderView::section::vertical +{ + border-top: 1px solid #353635; + +} + + +QHeaderView::section::horizontal::first, +QHeaderView::section::horizontal::only-one +{ + border-left: 1px solid #353635; + +} + + +QHeaderView::section::horizontal +{ + border-left: 1px solid #353635; + +} + + +QTableCornerButton::section +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(60, 60, 60, 255),stop:1 rgba(50, 50, 50, 255)); + border: 1px solid #000; + color: #fff; + +} + + +/*-----QTreeWidget-----*/ +QTreeView +{ + show-decoration-selected: 1; + alternate-background-color: #3a3a3a; + selection-color: #fff; + background-color: #2d2d2d; + border: 1px solid gray; + padding-top : 5px; + color: #fff; + font: 8pt; + +} + + +QTreeView::item:selected +{ + color:#fff; + background-color: #b78620; + border-radius: 0px; + +} + + +QTreeView::item:!selected:hover +{ + background-color: #262626; + border: none; + color: white; + +} + + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings +{ + image: url(://tree-closed.png); + +} + + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings +{ + image: url(://tree-open.png); + +} + + +/*-----QListView-----*/ +QListView +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(83, 83, 83, 255),stop:0.293269 rgba(81, 81, 81, 255),stop:0.634615 rgba(79, 79, 79, 255),stop:1 rgba(83, 83, 83, 255)); + border : none; + color: white; + show-decoration-selected: 1; + outline: 0; + border: 1px solid gray; + +} + + +QListView::disabled +{ + background-color: #656565; + color: #1b1b1b; + border: 1px solid #656565; + +} + + +QListView::item +{ + background-color: #2d2d2d; + padding: 1px; + +} + + +QListView::item:alternate +{ + background-color: #3a3a3a; + +} + + +QListView::item:selected +{ + background-color: #b78620; + border: 1px solid #b78620; + color: #fff; + +} + + +QListView::item:selected:!active +{ + background-color: #b78620; + border: 1px solid #b78620; + color: #fff; + +} + + +QListView::item:selected:active +{ + background-color: #b78620; + border: 1px solid #b78620; + color: #fff; + +} + + +QListView::item:hover { + background-color: #262626; + border: none; + color: white; + +} + + +/*-----QCheckBox-----*/ +QCheckBox +{ + background-color: transparent; + color: lightgray; + border: none; + +} + + +QCheckBox::indicator +{ + background-color: #323232; + border: 1px solid darkgray; + width: 12px; + height: 12px; + +} + + +QCheckBox::indicator:checked +{ + image:url("./ressources/check.png"); + background-color: #b78620; + border: 1px solid #3a546e; + +} + + +QCheckBox::indicator:unchecked:hover +{ + border: 1px solid #b78620; + +} + + +QCheckBox::disabled +{ + color: #656565; + +} + + +QCheckBox::indicator:disabled +{ + background-color: #656565; + color: #656565; + border: 1px solid #656565; + +} + + +/*-----QRadioButton-----*/ +QRadioButton +{ + color: lightgray; + background-color: transparent; + +} + + +QRadioButton::indicator::unchecked:hover +{ + background-color: lightgray; + border: 2px solid #b78620; + border-radius: 6px; +} + + +QRadioButton::indicator::checked +{ + border: 2px solid #b78620; + border-radius: 6px; + background-color: rgba(183,134,32,20%); + width: 9px; + height: 9px; + +} + + +/*-----QSlider-----*/ +QSlider::groove:horizontal +{ + background-color: transparent; + height: 3px; + +} + + +QSlider::sub-page:horizontal +{ + background-color: #b78620; + +} + + +QSlider::add-page:horizontal +{ + background-color: #131313; + +} + + +QSlider::handle:horizontal +{ + background-color: #b78620; + width: 14px; + margin-top: -6px; + margin-bottom: -6px; + border-radius: 6px; + +} + + +QSlider::handle:horizontal:hover +{ + background-color: #d89e25; + border-radius: 6px; + +} + + +QSlider::sub-page:horizontal:disabled +{ + background-color: #bbb; + border-color: #999; + +} + + +QSlider::add-page:horizontal:disabled +{ + background-color: #eee; + border-color: #999; + +} + + +QSlider::handle:horizontal:disabled +{ + background-color: #eee; + border: 1px solid #aaa; + border-radius: 3px; + +} + + +/*-----QScrollBar-----*/ +QScrollBar:horizontal +{ + border: 1px solid #222222; + background-color: #3d3d3d; + height: 15px; + margin: 0px 16px 0 16px; + +} + + +QScrollBar::handle:horizontal +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + min-height: 20px; + +} + + +QScrollBar::add-line:horizontal +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + width: 15px; + subcontrol-position: right; + subcontrol-origin: margin; + +} + + +QScrollBar::sub-line:horizontal +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + width: 15px; + subcontrol-position: left; + subcontrol-origin: margin; + +} + + +QScrollBar::right-arrow:horizontal +{ + image: url(://arrow-right.png); + width: 6px; + height: 6px; + +} + + +QScrollBar::left-arrow:horizontal +{ + image: url(://arrow-left.png); + width: 6px; + height: 6px; + +} + + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal +{ + background: none; + +} + + +QScrollBar:vertical +{ + background-color: #3d3d3d; + width: 16px; + border: 1px solid #2d2d2d; + margin: 16px 0px 16px 0px; + +} + + +QScrollBar::handle:vertical +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + min-height: 20px; + +} + + +QScrollBar::add-line:vertical +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + height: 15px; + subcontrol-position: bottom; + subcontrol-origin: margin; + +} + + +QScrollBar::sub-line:vertical +{ + background-color: qlineargradient(spread:repeat, x1:1, y1:0, x2:1, y2:1, stop:0 rgba(97, 97, 97, 255),stop:1 rgba(90, 90, 90, 255)); + border: 1px solid #2d2d2d; + height: 15px; + subcontrol-position: top; + subcontrol-origin: margin; + +} + + +QScrollBar::up-arrow:vertical +{ + image: url(://arrow-up.png); + width: 6px; + height: 6px; + +} + + +QScrollBar::down-arrow:vertical +{ + image: url(://arrow-down.png); + width: 6px; + height: 6px; + +} + + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical +{ + background: none; + +} + + +/*-----QProgressBar-----*/ +QProgressBar +{ + border: 1px solid #666666; + text-align: center; + color: #000; + font-weight: bold; + +} + + +QProgressBar::chunk +{ + background-color: #b78620; + width: 30px; + margin: 0.5px; + +} + diff --git a/MarkViewDesktop/build.bat b/MarkViewDesktop/build.bat index 5582e98..dce4243 100644 --- a/MarkViewDesktop/build.bat +++ b/MarkViewDesktop/build.bat @@ -1,5 +1,5 @@ @echo off -echo Starting Build mk.py with PyInstaller... -pyinstaller --noconsole --name DocViewer --icon="doc_icon.ico" --add-data "ui_docV.py;." --add-data "icons_svg/*;icons" mk.py +echo Starting Build DocViewer with PyInstaller... +pyinstaller --noconsole --name DocViewer --icon="doc_icon.ico" --add-data "ui_docV.py;." --add-data "icons_svg/*;icons" main.py echo Build complete. Press any key to exit. diff --git a/MarkViewDesktop/main.py b/MarkViewDesktop/main.py index abf5a3a..4b8c4a3 100644 --- a/MarkViewDesktop/main.py +++ b/MarkViewDesktop/main.py @@ -1,141 +1,681 @@ import sys -from PyQt5 import QtCore, QtGui, QtWidgets -from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QFileDialog, QWidget -from PyQt5.QtWebEngineWidgets import QWebEngineView -import markdown +import os +from PyQt5 import QtCore, QtGui +from PyQt5.QtCore import Qt, QEvent +from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit, + QMessageBox, QMenu, QFileDialog, QInputDialog, + QVBoxLayout, QWidget, QAction) +import markdown # necessario para as tabelas from ui_docV import Ui_MainWindow -from pathlib import Path class MarkdownEditor(QMainWindow): - file_path = "" - def __init__(self): - global file_path + + def __init__(self, file_to_open=None): super().__init__() - file_path = Path('mark_css.html') + self.html_text_ = "" + + self.complete_html = "" self.ui = Ui_MainWindow() self.ui.setupUi(self) self.initUI() - # ================================== Inicializa UI Main + self.setWindowFlags(Qt.FramelessWindowHint) + #self.setAttribute(Qt.WA_TranslucentBackground) + #==================================================== Connect window funtcions to buttons + self.ui.close_btn.clicked.connect(lambda: self.close()) + self.ui.minimize_btn.clicked.connect(lambda: self.showMinimized()) + self.ui.maxmize_btn.clicked.connect(lambda: self.restore_or_maximize()) + self._mousePressPos = None + # Verify thats app has initialized by and open_file request + if file_to_open: + self.open_file(file_to_open) + self.setFocus() + + #self.ui.splitter.splitterMoved.connect(self.checkSplitterSizes) + + def showEvent(self, event): + super().showEvent(event) + """Função chamada quando os componentes visuais ja foram carregados, + apos a chamada de show() + """ + # Agora a janela está completamente carregada e podemos verificar o splitter + #self.checkIfAnyItemHidden() + def initUI(self): - self.ui.statusBarMessage("[ ============================================= Powered by SherzoLambda =============================================]") - self.previewArea = QWebEngineView() - self.previewArea.setStyleSheet("border-radius:12px;") - self.ui.splitter.addWidget(self.previewArea) + self.ui.statusBarMessage() + self.ui.menu = self.create_menu() self.ui.splitter.setStyleSheet("QSplitter::handle {background-color:#dfe2e5 ; border: 8px ridge qlineargradient(spread:pad, x1:0.982591, y1:0.035, x2:0.273, y2:0.238636, stop:0.119318 rgba(199, 207, 255, 255), stop:1 rgba(139, 98, 155, 255)); }") self.ui.editArea.setFocus() - style_preview = """ - QWebEngineView { - border-radius: 10px; /* Raio de borda para arredondar */ - } - """ - - self.previewArea.setStyleSheet(style_preview) - # ======================================= Barra de Menu - menubar = self.menuBar() - fileMenu = menubar.addMenu('File') - - # ============ Abrir - openFile = QAction('Open', self) - openFile.triggered.connect(self.showDialog) - fileMenu.addAction(openFile) - - # ============ Salvar - saveFile = QAction('Save', self) - saveFile.triggered.connect(self.saveDialog) - fileMenu.addAction(saveFile) - - # ============ Fechar - exitAction = QAction('Exit', self) - exitAction.triggered.connect(self.close) - fileMenu.addAction(exitAction) - - # ============ Atualizar a visualização ao vivo durante a edição - self.ui.editArea.textChanged.connect(self.updatePreview) - + self.ui.previewArea.setZoomFactor(0.8) self.setGeometry(100, 100, 800, 600) - self.setWindowTitle('DocViewer Palace') + self.setWindowTitle('DocViewer') - # Inicializar a visualização - self.updatePreview() + self.ui.file_btn.clicked.connect(self.show_menu) + self.ui.tab_widget.tabCloseRequested.connect(self.close_tab) + self.ui.tab_widget.currentChanged.connect(self.onTabChange) + self.ui.tab_widget.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) + self.ui.tab_widget.tabBar().customContextMenuRequested.connect(self.onTabRightClick) + + def create_menu(self): + menu = QMenu() + menu.setStyleSheet("QMenu { font-size: 12px; }") # Aumentando o tamanho da fonte - self.ui.editArea.setAcceptRichText(False) # Desabilita a formatação rica de texto - self.ui.editArea.installEventFilter(self) + # Adicionando ações ao menu com atalhos + new_file_act = menu.addAction("Novo arquivo") + new_file_act.setShortcut("Ctrl+N") # Atalho + new_file_act.triggered.connect(self.new_file) + menu.addAction(new_file_act) + + op_file_act = menu.addAction("Abrir Arquivo") + op_file_act.setShortcut("Ctrl+O") # Atalho + op_file_act.triggered.connect(self.showDialogAndOpenFile) + menu.addAction(op_file_act) + + save_file_act = menu.addAction("Salvar Arquivo") + save_file_act.setShortcut("Ctrl+S") + save_file_act.triggered.connect(self.saveFile) + menu.addAction(save_file_act) + # Ajustando a largura do menu + menu.setFixedWidth(210) # Definindo uma largura fixa para o menu + + return menu + + def show_menu(self): + if not self.ui.has_op_menu: + self.ui.menu.exec_(self.ui.file_btn.mapToGlobal(self.ui.file_btn.rect().bottomLeft())) # Exibir o menu abaixo do botão + self.ui.has_op_menu = True + else: + # Se o menu está aberto, fecha-o + #print("Fechando o menu...") + self.ui.menu.close() + #self.menu = None + self.ui.has_op_menu = False + #======================================== Gerencia uso dos atalhos + def keyPressEvent(self, event): + if event.type() == QEvent.KeyPress: + if event.key() == Qt.Key_N and event.modifiers() == Qt.ControlModifier: + self.new_file() + elif event.key() == Qt.Key_O and event.modifiers() == Qt.ControlModifier: + self.showDialogAndOpenFile() + elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier: + self.saveFile() + # elif event.key() == Qt.Key_Space and event.modifiers() == Qt.ControlModifier: + # self.renderPreview() + super().keyPressEvent(event) + #======================================== Mouse events para tratar redimensionamentos e Reposicionamentos + def mousePressEvent(self, event): + # Armazena a posição do mouse quando pressionado + if event.button() == Qt.LeftButton: + self._mousePressPos = event.pos() + def mouseMoveEvent(self, event): + # Move a janela com base na posição do mouse + if self._mousePressPos is not None: + self.move(self.pos() + event.pos() - self._mousePressPos) + + def mouseReleaseEvent(self, event): + # Reseta a posição do mouse ao soltar + self._mousePressPos = None + #======================================== Gerencia{ Maximizar, miniminizar, Fechar} janela + def restore_or_maximize(self): + if self.isMaximized(): + self.showNormal() + else: + self.showMaximized() + + def closeEvent(self, event): + current_index = self.ui.tab_widget.currentIndex() + # Método para interceptar o fechamento da janela + if self.getCurrentFileModifiedStatus(current_index): + reply = QMessageBox.question( + self, 'Alterações não salvas', + "Você tem alterações não salvas. Deseja salvar antes de sair?", + QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel + ) + + if reply == QMessageBox.Yes: + self.saveFile() + elif reply == QMessageBox.Cancel: + event.ignore() # Cancela o fechamento da janela + return + event.accept() # Fecha a janela + #======================================== Atualização da vizualização def eventFilter(self, obj, event): - if obj == self.ui.editArea and event.type() == QtCore.QEvent.KeyPress and event.key() == QtCore.Qt.Key_Return: + if obj == self.ui.editArea and event.type() == QtCore.QEvent.KeyRelease and event.key() == QtCore.Qt.Key_Return: self.inteliComplete() return super().eventFilter(obj, event) - + def inteliComplete(self): cursor = self.ui.editArea.textCursor() - cursor.movePosition(QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.MoveAnchor) # Move o cursor para o início do documento - if cursor.position() == 0: # Verifica se o cursor está no início do documento - print("Auto completar: Início do documento") - previous_line = cursor.block().text().strip() # Obtém o texto da linha anterior - print("Cursor position:", cursor.position()) - print("Previous line:", previous_line) - return - else: - cursor.movePosition(QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.MoveAnchor) - #cursor.movePosition(QtGui.QTextCursor.PreviousBlock, QtGui.QTextCursor.MoveAnchor) - previous_line = cursor.block().text().strip() # Obtém o texto da linha anterior - print("Cursor position:", cursor.position()) - print("Previous line:", previous_line) - if previous_line.startswith("#"): - cursor.insertText("#") - elif previous_line.startswith("-"): - cursor.insertText("-") - elif previous_line.startswith(">"): - cursor.insertText(">") - elif previous_line.startswith("_"): - cursor.insertText("_") - elif previous_line.startswith("*"): - cursor.insertText("*") - elif previous_line.startswith("1."): - cursor.insertText("1.") - elif previous_line.startswith("- [ ]"): - cursor.insertText("- [ ]") - print("Auto completar: Nenhuma ação necessária") - return # Retorna sem fazer nada - if cursor.positionInBlock() == 0: # Verifica se o cursor está no início de uma linha - print("Auto completar: Início da linha") - cursor.movePosition(QtGui.QTextCursor.PreviousBlock, QtGui.QTextCursor.MoveAnchor) - previous_line = cursor.block().text().strip() # Obtém o texto da linha anterior - print("Cursor position:", cursor.position()) - print("Previous line:", previous_line) - if previous_line.startswith("#"): - print("Auto completar: Cabeçalho detectado") + cursor.movePosition(QtGui.QTextCursor.EndOfLine) # Move o cursor para o final da linha atual + + actual_line = cursor.block().text().strip() + previous_line = cursor.block().previous().text().strip() # Obtém o texto da linha anterior + #next_line = cursor.block().next().text().strip() + #print("actual line", actual_line) + if previous_line.startswith("-"): + if(len(previous_line) > 2 and previous_line[2] == '['): + cursor.insertText("- [ ] ") else: - print("Auto completar: Nenhuma ação necessária") - return # Retorna sem fazer nada - - def showDialog(self): - fname = QFileDialog.getOpenFileName(self, 'Open file', '') - - if fname[0]: - with open(fname[0], 'r') as file: - self.textEdit.setPlainText(file.read()) - - def saveDialog(self): - fname = QFileDialog.getSaveFileName(self, 'Save file', '') - - if fname[0]: - with open(fname[0], 'w') as file: - file.write(self.textEdit.toPlainText()) - - def updatePreview(self): - markdown_text = self.ui.editArea.toPlainText() - html_text = markdown.markdown(markdown_text, extensions=['extra', 'tables']) - #print(html_text) - html_text = html_text.replace('[ ]', '') - html_text = html_text.replace('[x]', '') - html_content = file_path.read_text(encoding='utf-8') - complete_html = html_content.format(html_text=html_text) - print(complete_html) - self.previewArea.setHtml(complete_html) + if(actual_line.startswith("-")): + cursor.movePosition(QtGui.QTextCursor.EndOfLine) + else: + cursor.insertText("- ") + elif previous_line.startswith(">"): + cursor.insertText("> ") + elif previous_line.startswith("_"): + cursor.insertText("_ ") + elif previous_line.startswith("*"): + cursor.insertText("* ") + elif previous_line.startswith("1."): + cursor.insertText("1. ") + elif previous_line.startswith("- "): + cursor.insertText("- [ ] ") + + ##cursor.movePosition(QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.MoveAnchor) + self.ui.editArea.setTextCursor(cursor) + + def getMarkdownText(self, input_text): + mkd_text = markdown.markdown(input_text, extensions=['extra', 'tables','fenced_code', 'codehilite']) + mkd_text = mkd_text.replace('[ ]', '') # Necessário! + mkd_text = mkd_text.replace('[x]', '') # Necessário! + return mkd_text + + def updatePreview(self, text_edit): + """Atualiza a visualização com base no conteúdo do QTextEdit fornecido""" + markdown_text = text_edit.toPlainText() + self.verifyChangesAndSetTabName() + + self.html_text_ = self.getMarkdownText(markdown_text) + self.updateCompleteHtml() + self.ui.previewArea.setHtml(self.complete_html) + ### TODO: Permitir ativar e desativar esta funcionalidade + ##self.ui.previewArea.loadFinished.connect(self.scroll_to_bottom) + + def scroll_to_bottom(self): + # Executa um scroll até o final da área de visualização + scroll_script = "window.scrollTo(0, document.body.scrollHeight);" + self.ui.previewArea.page().runJavaScript(scroll_script) + def updateAfterTabChange(self, textEdit): + plain_text = textEdit.toPlainText() + self.html_text_ = self.getMarkdownText(plain_text) + self.updateCompleteHtml() + self.ui.previewArea.setHtml(self.complete_html) + + def updateCompleteHtml(self): + """Atualiza e redefine o conteúdo de complete_html""" + self.complete_html = f""" + + + + + + + + {self.html_text_} + + + """ + + #======================================== Mudança de Aba + def verifyChangesAndSetTabName(self): + current_index = self.ui.tab_widget.currentIndex() + current_tab = self.ui.tab_widget.widget(current_index) + if current_tab in self.ui.open_files: + # Atualiza o estado de isModified para True + file_name = self.ui.open_files[current_tab][0] + isModified = self.ui.open_files[current_tab][1] + if not file_name.endswith('*') and not isModified: + self.ui.tab_widget.setTabText(current_index, f"{os.path.basename(file_name)}*") + self.ui.open_files[current_tab][1] = True + + def getCurrentTextEdit(self, current_index): + """Recupera o QTextEdit da aba atualmente selecionada""" + # Obtém o índice da aba atualmente selecionada + #current_index = self.ui.tab_widget.currentIndex() + + if current_index == -1: + # Nenhuma aba selecionada + return None + + # Obtém o widget da aba selecionada + current_tab = self.ui.tab_widget.widget(current_index) + + if current_tab is not None: + # Procura pelo QTextEdit dentro do layout da aba + layout = current_tab.layout() + if layout is not None and layout.count() > 0: + # Assume que o QTextEdit é o primeiro widget no layout + text_edit = layout.itemAt(0).widget() + if isinstance(text_edit, QTextEdit): + return text_edit + + return None + + def onTabChange(self, index): + self.ui.editArea = self.getCurrentTextEdit(index) + if(self.ui.editArea is not None): + self.updateAfterTabChange(self.ui.editArea) + self.ui.editArea.installEventFilter(self) + #print(f"Aba mudada: {index}") + + def onTabRightClick(self, position): + """Exibe um diálogo para renomear arquivo ao clicar com o botão direito em cima da aba""" + tab_index = self.ui.tab_widget.tabBar().tabAt(position) + tab_widget = self.ui.tab_widget.widget(tab_index) + if tab_index != -1: + # Cria o menu de contexto + menu = QMenu(self) + + rename_action = QAction("Renomear arquivo", self) + rename_action.triggered.connect(lambda: self.renameTab(tab_index, tab_widget)) + + open_action = QAction("Abrir aqui", self) + open_action.triggered.connect(lambda: self.open_here(tab_index)) + + save_action = QAction("Salvar arquivo", self) + save_action.triggered.connect(lambda: self.saveFile()) + + # Adiciona as ações ao menu + menu.addAction(rename_action) + menu.addAction(open_action) + menu.addAction(save_action) + #menu.addSeparator() # Adiciona um separador visual + + + # Exibe o menu de contexto na posição do cursor + menu.exec_(self.ui.tab_widget.tabBar().mapToGlobal(position)) + + def renameTab(self, tab_index, tab_widget): + """Renomeia a aba especificada e o arquivo associado""" + # Obtém o nome atual da aba e o caminho completo do arquivo + current_name = self.ui.tab_widget.tabText(tab_index) + file_path = self.ui.open_files[tab_widget][0] # Caminho atual do arquivo + current_dir = os.path.dirname(file_path) # Diretório atual do arquivo + + # Solicita um novo nome para o arquivo + new_name, ok = QInputDialog.getText(self, "Renomear arquivo", + "Novo nome para o arquivo:", + text=os.path.basename(current_name)) + if ok and new_name.strip(): + # Gera o novo caminho completo do arquivo + new_file_path = os.path.join(current_dir, new_name.strip()) + + try: + # Renomeia o arquivo no sistema de arquivos + os.rename(file_path, new_file_path) + + # Atualiza o nome da aba + self.ui.tab_widget.setTabText(tab_index, new_name.strip()) + + # Atualiza o nome no dicionário de arquivos abertos + self.ui.open_files[tab_widget][0] = new_file_path + + print(f"Arquivo renomeado com sucesso para: {new_file_path}") + except Exception as e: + print(f"Erro ao renomear o arquivo: {e}") + + def getCurrentFileModifiedStatus(self, current_index): + """Recupera apenas o status de modificação da aba atualmente selecionada""" + if current_index == -1: + # Nenhuma aba selecionada + return None + + # Obtém o widget da aba selecionada + current_tab = self.ui.tab_widget.widget(current_index) + + if current_tab is not None: + # Verifica se a aba atual está no dicionário de arquivos abertos + if current_tab in self.ui.open_files: + file_info = self.ui.open_files[current_tab] + is_modified = file_info[1] # Status de modificação + return is_modified + + return None + + def checkIfAnyItemHidden(self): + """Verifica se algum item no QSplitter tem altura 0 (não está visível) e ajusta sua altura""" + sizes = self.ui.splitter.sizes() # Obtém as alturas dos widgets no QSplitter + updated = False + # Define as alturas específicas para os itens + if len(sizes) >= 2: + sizes[0] = 186 # Define a altura do primeiro widget para 186 + sizes[1] = 288 # Define a altura do segundo widget para 288 + + # Atualiza os tamanhos no QSplitter + self.ui.splitter.setSizes(sizes) + #print("Alturas ajustadas: Item 0 = 186, Item 1 = 288") + else: + #print("O QSplitter não possui widgets suficientes para ajustar as alturas.") + updated = True + + if updated: + # Atualiza os tamanhos no QSplitter com os novos valores + self.ui.splitter.setSizes(sizes) + #print("Altura ajustada para itens escondidos.") + + def close_tab(self, index): + """Fecha a aba na posição fornecida, verificando se há alterações não salvas""" + widget = self.ui.tab_widget.widget(index) + + if self.check_unsaved_changes(index): + return # Se o usuário cancelar, não fecha a aba + + # Remove a aba + self.ui.tab_widget.removeTab(index) + + # Remove o widget da lista de arquivos abertos + if widget in self.ui.open_files: + del self.ui.open_files[widget] + #======================================== Lida com arquivos {Abertura, escrita} + #============== Salvamento de arquivos + def check_unsaved_changes(self, tab_index): + """Verifica se a aba atual tem alterações não salvas e oferece para salvar""" + widget = self.ui.tab_widget.widget(tab_index) + if not widget: + return False + + text_edit = widget.findChild(QTextEdit) + + if text_edit and text_edit.document().isModified(): + # Pergunta ao usuário se ele quer salvar as alterações + reply = QMessageBox.question(self, "Arquivo Modificado", + "O arquivo foi modificado. Deseja salvar as alterações?", + QMessageBox.Yes | QMessageBox.No |QMessageBox.Cancel) + + if reply == QMessageBox.Yes: + return self.save_file(tab_index) + elif reply == QMessageBox.Cancel: + return True # Cancela a ação + return False + + def saveFileDialog(self): + # Define o filtro para apenas arquivos .md + filter = "Markdown Files (*.md);;All Files (*)" + fname, _ = QFileDialog.getSaveFileName(self, 'Save file', '', filter) + current_index = self.ui.tab_widget.currentIndex() + current_tab = self.ui.tab_widget.widget(current_index) + if fname: + # Adiciona a extensão .md se não estiver presente + if not fname.endswith('.md'): + fname += '.md' + with open(fname, 'w', encoding='utf-8') as file: + file.write(self.ui.editArea.toPlainText()) + # Atualiza o nome da aba atual para o nome do arquivo salvo + self.ui.open_files[current_tab][1] = False + self.ui.open_files[current_tab][2] = False + file_name = fname.split('/')[-1] # Extrai o nome do arquivo do caminho + self.ui.tab_widget.setTabText(current_index, file_name) + self.ui.tab_widget.setTabToolTip(current_index, fname) + + def saveFile(self): + """Salva as mudanças no arquivo atual""" + # Obtém a aba atual e o caminho do arquivo associado a ela + current_index = self.ui.tab_widget.currentIndex() + current_tab = self.ui.tab_widget.widget(current_index) + + file_info = self.ui.open_files[current_tab] + file_path = file_info[0] + isNewFile = file_info[2] + + if not isNewFile: + + if file_path: + # Sobrescreve o arquivo atual com o conteúdo do QTextEdit + text_edit = current_tab.layout().itemAt(0).widget() + if isinstance(text_edit, QTextEdit): + with open(file_path, 'w', encoding='utf-8') as file: + file.write(text_edit.toPlainText()) + self.ui.open_files[current_tab][1] = False + + # Remove o '*' do nome da aba + clean_file_name = os.path.basename(file_path) # Apenas o nome do arquivo sem o caminho + self.ui.tab_widget.setTabText(current_index, clean_file_name) + #print(f"Arquivo {file_path} salvo com sucesso.") + else: + # Caso não haja um caminho, abre um diálogo para salvar como um novo arquivo + self.saveFileDialog() + #============== Abertura de um arquivo/Novo Arquivo + def open_here(self, tab_index): + """Abre um arquivo na aba selecionada, substituindo o conteúdo atual""" + # Abre um diálogo para selecionar o arquivo + options = QFileDialog.Options() + file_path, _ = QFileDialog.getOpenFileName(self, "Abrir Arquivo", "", "Todos os Arquivos (*);;Arquivos de Texto (*.txt);;Markdown Files (*.md)", options=options) + + if file_path: + try: + # Carrega o conteúdo do arquivo selecionado + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + # Obtém o widget da aba selecionada + current_tab = self.ui.tab_widget.widget(tab_index) + if current_tab: + # Procura pelo QTextEdit dentro do layout da aba + layout = current_tab.layout() + if layout is not None and layout.count() > 0: + # Assume que o QTextEdit é o primeiro widget no layout + text_edit = layout.itemAt(0).widget() + if isinstance(text_edit, QTextEdit): + # Substitui o conteúdo do QTextEdit pelo conteúdo do arquivo + text_edit.setPlainText(content) + + # Atualiza o nome da aba com o nome do arquivo aberto + file_name = os.path.basename(file_path) + self.ui.tab_widget.setTabText(tab_index, file_name) + self.ui.tab_widget.setTabToolTip(tab_index, file_path) + + # Atualiza o dicionário de arquivos abertos + self.ui.open_files[current_tab] = [file_name, False, False] + + print(f"Arquivo {file_name} aberto na aba {tab_index} com sucesso.") + except Exception as e: + print(f"Erro ao abrir o arquivo: {e}") + + def showDialogAndOpenFile(self): + options = QFileDialog.Options() + options |= QFileDialog.ReadOnly # Abre o arquivo em modo somente leitura + filter = "Markdown Files (*.md);;All Files (*)" + file_path, _ = QFileDialog.getOpenFileName(self, "Abrir Arquivo", "",filter,options=options) + + file_name = "" + if file_path: + file_name = os.path.basename(file_path) + # Carregar o conteúdo do arquivo ou realizar alguma ação com ele + with open(file_path, 'r',encoding='utf-8') as file: + # content = file.read() + # self.ui.previewArea.setPlainText(content) + self.open_file(file, file_name, file_path) + + def open_file(self, file_, file_name, file_path=None): + """Cria uma nova aba com um QTextEdit para abrir arquivo selecionado""" + new_tab = QWidget() + layout = QVBoxLayout() + + # Criando uma área de texto + text_edit = QTextEdit() # TextEditWithLineNumbers() + text_edit.setStyleSheet("background-color: #DCDCDC;") + text_edit.setTabStopDistance(32) + layout.addWidget(text_edit) + layout.setContentsMargins(4, 4, 4, 4) + new_tab.setLayout(layout) + text_edit.textChanged.connect(lambda: self.updatePreview(text_edit)) + text_edit.setPlainText(file_.read()) + + # Adiciona uma nova aba com o editor de texto e o nome do arquivo como título da aba + tab_index = self.ui.tab_widget.addTab(new_tab, file_name) + self.ui.tab_widget.setCurrentIndex(tab_index) + if(file_path): + self.ui.tab_widget.setTabToolTip(tab_index,file_path) + text_edit.setFocus() + + # Armazena o caminho do arquivo no widget da aba como chave + self.ui.open_files[new_tab] = [file_.name, False, False] + self.checkIfAnyItemHidden() + + def new_file(self): + """Cria uma nova aba com um QTextEdit para um novo arquivo""" + new_tab = QWidget() + layout = QVBoxLayout() + + # Criando uma área de texto + text_edit = QTextEdit() #TextEditWithLineNumbers() + text_edit.setStyleSheet("background-color: #DCDCDC;") + text_edit.setTabStopDistance(32) + layout.addWidget(text_edit) + layout.setContentsMargins(4, 4, 4, 4) + new_tab.setLayout(layout) + text_edit.textChanged.connect(lambda: self.updatePreview(text_edit)) + # Gera um nome único para o novo arquivo + new_file_name = self.generate_new_file_name() + # Adiciona uma nova aba com o editor de texto + tab_index = self.ui.tab_widget.addTab(new_tab, new_file_name) + self.ui.tab_widget.setCurrentIndex(tab_index) + text_edit.setFocus() + #============================= [, isModified, isNewFile] + self.ui.open_files[new_tab] = [new_file_name, False, True] + self.checkIfAnyItemHidden() + + def generate_new_file_name(self): + """Gera um nome de arquivo novo único com base no número de arquivos novos""" + self.ui.new_file_count += 1 + base_name = "Novo Arquivo" + file_name = f"{base_name} ({self.ui.new_file_count})" + + # Garante que o nome é único entre as abas + existing_tabs = [self.ui.tab_widget.tabText(i) for i in range(self.ui.tab_widget.count())] + while file_name in existing_tabs: + self.ui.new_file_count += 1 + file_name = f"{base_name} ({self.ui.new_file_count})" + return file_name if __name__ == '__main__': app = QApplication(sys.argv) - ex = MarkdownEditor() - ex.show() + #styleSheetFile = QFile("./docV.qss") + #if styleSheetFile.open(QFile.ReadOnly): + # styleSheet = str(styleSheetFile.readAll(), encoding='utf-8') # Leitura e conversão para string + # app.setStyleSheet(styleSheet) + # styleSheetFile.close() + window = MarkdownEditor() + window.show() sys.exit(app.exec_()) diff --git a/MarkViewDesktop/ui_docV.py b/MarkViewDesktop/ui_docV.py index 04d6e81..71ea8e0 100644 --- a/MarkViewDesktop/ui_docV.py +++ b/MarkViewDesktop/ui_docV.py @@ -1,14 +1,24 @@ +import sys +from os import path from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QSplitter, QLabel, QSpinBox, QComboBox -from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QPixmap, QPainter, QFont, QTextCursor from PyQt5.QtSvg import QSvgRenderer +from PyQt5.QtWebEngineWidgets import QWebEngineView +def resource_path(relative_path): + """ Get the absolute path to the resource, works for dev and for PyInstaller """ + try: + base_path = sys._MEIPASS + except AttributeError: + base_path = path.abspath(".") + + return path.join(base_path, relative_path) def loadSvgIcon(file_path, width=80, height=80): svg_renderer = QSvgRenderer(file_path) pixmap = QPixmap(width, height) - pixmap.fill(Qt.transparent) + pixmap.fill(QtCore.Qt.transparent) painter = QPainter(pixmap) svg_renderer.render(painter) painter.end() @@ -33,156 +43,265 @@ class Ui_MainWindow(object): /* Cor de fundo padrão */ border: 2px solid #161b22; /* Borda */ color: white; /* Cor do texto */ - + font-size: 16px; border-radius: 4px; /* Borda arredondada */ """ - + style_closeBTN = """ + QPushButton { + border: none; + background-color: transparent; + } + QPushButton:hover { + background-color: #f4696b; /* cor de fundo*/ + } + """ + style_m_M = """ + QPushButton { + font-size: 18px; + border: none; + background-color: transparent; + } + QPushButton:hover { + background-color: #DCDCDC; + } +""" + style = f""" + QTabWidget::pane {{ + background-image: url({iconspath+"docV_icon.png"}); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + }} + """ def setupUi(self, MainWindow): + self.new_file_count = 0 + self.open_files = {} + self.menu = None + self.has_op_menu = False + self.act_op_file = None MainWindow.setObjectName("MainWindow") - MainWindow.resize(539, 307) - MainWindow.setStyleSheet("background-color: rgb(117, 117, 117); border-radius:12px;") + MainWindow.setStyleSheet("background-color: rgb(117, 117, 117);") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) - self.verticalLayout_2.setObjectName("verticalLayout_2") + self.centralVL = QtWidgets.QVBoxLayout(self.centralwidget) + self.centralVL.setObjectName("centralVL") + #self.centralVL.setSpacing(0) + self.centralVL.setContentsMargins(0, 0, 0, 0) + + # Frames for layouts + # header_frame, buttons_frame + self.header_frame = QtWidgets.QFrame(self.centralwidget) + self.header_frame.setStyleSheet("background-color: rgb(117, 117, 117);") + self.header_frame.setObjectName("header_frame") + self.buttons_frame = QtWidgets.QFrame(self.centralwidget) + self.buttons_frame.setStyleSheet("background-color: rgb(117, 117, 117);") + self.buttons_frame.setObjectName("buttons_frame") + # Layouts for components + # editButtonsHL, headerVL, headerLayout + self.headerVL = QtWidgets.QVBoxLayout(self.header_frame) + self.headerVL.setObjectName("headerVL") + self.editButtonsHL = QtWidgets.QHBoxLayout(self.buttons_frame) + self.editButtonsHL.setObjectName("editButtonsHL") + self.editButtonsHL.setSpacing(10) + + self.headerVL.setContentsMargins(0, 0, 0, 0) + #self.editButtonsHL.setContentsMargins(0, 0, 0, 0) + + # ===================== Header for headerVL + self.headerLayout = QtWidgets.QHBoxLayout() + self.headerLayout.setObjectName("headerLayout") + self.headerLayout.setSpacing(0) + self.headerLayout.setContentsMargins(0, 0, 0, 0) + # ICON label + self.icon_label = QtWidgets.QLabel(self.header_frame) + self.icon_label.setPixmap(QPixmap(resource_path('icons/docV_icon.png')).scaled(35, 35, aspectRatioMode=QtCore.Qt.KeepAspectRatio)) + self.icon_label.setMaximumHeight(30) + self.icon_label.setMaximumWidth(45) + self.icon_label.setContentsMargins(8,0,0,0) + # FILE button + self.file_btn = QtWidgets.QPushButton(self.header_frame) + self.file_btn.setStyleSheet(self.style_m_M) + self.file_btn.setObjectName("file_btn") + self.file_btn.setText("File") + self.file_btn.setMaximumHeight(30) + self.file_btn.setMaximumWidth(45) - # Create the input frame with buttons - self.frame_input = QtWidgets.QFrame(self.centralwidget) - self.frame_input.setStyleSheet("background-color: rgb(117, 117, 117);") - self.frame_input.setObjectName("frame_input") - self.verticalLayout = QtWidgets.QVBoxLayout(self.frame_input) - self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.horizontalLayout.setSpacing(10) - self.verticalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) - # ============================================ - self.fontSize_sp = QSpinBox(self.frame_input) + spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + # CLOSE WINDOW button + self.close_btn = QtWidgets.QPushButton(self.header_frame) + self.close_btn.setIcon(QIcon(resource_path('icons/close.png'))) + self.close_btn.setObjectName("close_btn") + self.close_btn.setStyleSheet(self.style_closeBTN) + self.close_btn.setMaximumHeight(30) + self.close_btn.setMaximumWidth(45) + #self.close_btn.setToolTip("Close Window") + # MINIMIZE WINDOW button + self.minimize_btn = QtWidgets.QPushButton(self.header_frame) + self.minimize_btn.setIcon(QIcon(resource_path('icons/mini2.png'))) + self.minimize_btn.setObjectName("close_btn") + self.minimize_btn.setStyleSheet(self.style_m_M) + self.minimize_btn.setMaximumHeight(30) + self.minimize_btn.setMaximumWidth(45) + #self.minimize_btn.setToolTip("Minimize Window") + # MAXIMIZE WINDOW button + self.maxmize_btn = QtWidgets.QPushButton(self.header_frame) + self.maxmize_btn.setIcon(QIcon(resource_path('icons/maximizar.png'))) + self.maxmize_btn.setObjectName("maxmize_btn") + self.maxmize_btn.setStyleSheet(self.style_m_M) + self.maxmize_btn.setMaximumHeight(30) + self.maxmize_btn.setMaximumWidth(45) + #self.maxmize_btn.setToolTip("Maxmize Window") + # Adjusting size and alignment + self.file_btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + self.minimize_btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + self.maxmize_btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + self.close_btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + + self.headerLayout.setAlignment(self.file_btn, QtCore.Qt.AlignLeft) + self.headerLayout.setAlignment(self.minimize_btn, QtCore.Qt.AlignRight) + self.headerLayout.setAlignment(self.maxmize_btn, QtCore.Qt.AlignRight) + self.headerLayout.setAlignment(self.close_btn, QtCore.Qt.AlignRight) + # adding widgets to layout + self.headerLayout.addWidget(self.icon_label) + self.headerLayout.addWidget(self.file_btn) + self.headerLayout.addItem(spacer) + self.headerLayout.addWidget(self.minimize_btn) + self.headerLayout.addWidget(self.maxmize_btn) + self.headerLayout.addWidget(self.close_btn) + + self.headerVL.addLayout(self.headerLayout) + self.centralVL.addWidget(self.header_frame) + + # ================= Fonte style and Size =================== + self.fontSize_sp = QSpinBox(self.buttons_frame) self.fontSize_sp.setStyleSheet(self.style_utils) self.fontSize_sp.setToolTip("Tamanho do texto") self.fontSize_sp.setRange(8, 72) # Define o intervalo do tamanho da fonte self.fontSize_sp.setValue(16) # Define o valor padrão self.fontSize_sp.valueChanged.connect(self.update_font_size) # Conecta a mudança de valor ao método - self.fontStyle_cb = QComboBox(self.frame_input) - self.fontStyle_cb.setToolTip("Estilo da fonte") + self.fontStyle_cb = QComboBox(self.buttons_frame) + self.fontStyle_cb.setToolTip("Fonte do texto") + self.fontStyle_cb.setMaximumWidth(110) self.fontStyle_cb.setStyleSheet(self.style_utils) self.fontStyle_cb.addItems(["Arial", "Courier New", "Times New Roman"]) self.fontStyle_cb.currentIndexChanged.connect(self.update_font_style) - self.horizontalLayout.addWidget(self.fontStyle_cb) - self.horizontalLayout.addWidget(self.fontSize_sp) - # ==================================================== - # ======================================= Definindo botoes de auxilio a sintaxe Markdown - self.heading_btn = QtWidgets.QPushButton(self.frame_input) + + self.editButtonsHL.addWidget(self.fontStyle_cb) + self.editButtonsHL.addWidget(self.fontSize_sp) + + # ================= Markdown Utilities ======================== + self.heading_btn = QtWidgets.QPushButton(self.buttons_frame) self.heading_btn.setObjectName("heading_btn") self.heading_btn.setStyleSheet(self.style_button) self.heading_btn.setMinimumHeight(25) - self.horizontalLayout.addWidget(self.heading_btn) - self.heading_btn.setIcon(QIcon(loadSvgIcon(self.iconspath+"bx-heading.svg"))) + self.editButtonsHL.addWidget(self.heading_btn) + self.heading_btn.setIcon(QIcon(loadSvgIcon(resource_path('icons/bx-heading.svg')))) self.heading_btn.setToolTip("Header text") - self.bold_btn = QtWidgets.QPushButton(self.frame_input) + self.bold_btn = QtWidgets.QPushButton(self.buttons_frame) self.bold_btn.setObjectName("bold_btn") self.bold_btn.setMinimumHeight(25) self.bold_btn.setStyleSheet(self.style_button) - self.bold_btn.setIcon(QIcon(loadSvgIcon(self.iconspath+"bold.svg"))) + self.bold_btn.setIcon(QIcon(loadSvgIcon(resource_path('icons/bold.svg')))) self.bold_btn.setToolTip("Bold text") - self.horizontalLayout.addWidget(self.bold_btn) + self.editButtonsHL.addWidget(self.bold_btn) - self.italic_btn = QtWidgets.QPushButton(self.frame_input) + self.italic_btn = QtWidgets.QPushButton(self.buttons_frame) self.italic_btn.setObjectName("italic_btn") self.italic_btn.setStyleSheet(self.style_button) self.italic_btn.setMinimumHeight(25) self.italic_btn.setMinimumWidth(15) - self.italic_btn.setIcon(QIcon(loadSvgIcon(self.iconspath+"bx-italic.svg"))) + self.italic_btn.setIcon(QIcon(loadSvgIcon(resource_path('icons/bx-italic.svg')))) self.italic_btn.setToolTip("Italic Text") - self.horizontalLayout.addWidget(self.italic_btn) + self.editButtonsHL.addWidget(self.italic_btn) - self.quote_btn = QtWidgets.QPushButton(self.frame_input) + self.quote_btn = QtWidgets.QPushButton(self.buttons_frame) self.quote_btn.setObjectName("quote_btn") self.quote_btn.setStyleSheet(self.style_button) self.quote_btn.setMinimumHeight(25) - self.quote_btn.setIcon(QIcon(loadSvgIcon(self.iconspath+"bxs-quote-right.svg"))) + self.quote_btn.setIcon(QIcon(loadSvgIcon(resource_path('icons/bxs-quote-right.svg')))) self.quote_btn.setToolTip("Block Quote") - self.horizontalLayout.addWidget(self.quote_btn) + self.editButtonsHL.addWidget(self.quote_btn) - self.link_btn = QtWidgets.QPushButton(self.frame_input) + self.link_btn = QtWidgets.QPushButton(self.buttons_frame) self.link_btn.setObjectName("link_btn") self.link_btn.setStyleSheet(self.style_button) self.link_btn.setMinimumHeight(25) - self.link_btn.setIcon(QIcon(loadSvgIcon(self.iconspath+"bx-link.svg"))) + self.link_btn.setIcon(QIcon(loadSvgIcon(resource_path('icons/bx-link.svg')))) self.link_btn.setToolTip("refer a link") - self.horizontalLayout.addWidget(self.link_btn) + self.editButtonsHL.addWidget(self.link_btn) - self.unList_btn = QtWidgets.QPushButton(self.frame_input) + self.unList_btn = QtWidgets.QPushButton(self.buttons_frame) self.unList_btn.setObjectName("unList_btn") self.unList_btn.setStyleSheet(self.style_button) self.unList_btn.setMinimumHeight(25) - icon = QtGui.QIcon(self.iconspath+"menu.png") + icon = QtGui.QIcon(resource_path('icons/menu.png')) pixmap = icon.pixmap(QtCore.QSize(100, 100)) pixmap = pixmap.scaled(128, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) # Redimensiona a imagem self.unList_btn.setIcon(QtGui.QIcon(pixmap)) self.unList_btn.setToolTip("Unordered List") - self.horizontalLayout.addWidget(self.unList_btn) + self.editButtonsHL.addWidget(self.unList_btn) - self.nList_btn = QtWidgets.QPushButton(self.frame_input) + self.nList_btn = QtWidgets.QPushButton(self.buttons_frame) self.nList_btn.setObjectName("nList_btn") self.nList_btn.setStyleSheet(self.style_button) self.nList_btn.setMinimumHeight(25) - icon = QtGui.QIcon(self.iconspath+"number.png") + icon = QtGui.QIcon(resource_path('icons/number.png')) pixmap = icon.pixmap(QtCore.QSize(100, 100)) pixmap = pixmap.scaled(128, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) # Redimensiona a imagem self.nList_btn.setIcon(QtGui.QIcon(pixmap)) self.nList_btn.setText("") self.nList_btn.setToolTip("Numbered List") - self.horizontalLayout.addWidget(self.nList_btn) + self.editButtonsHL.addWidget(self.nList_btn) - self.taskList_btn = QtWidgets.QPushButton(self.frame_input) + self.taskList_btn = QtWidgets.QPushButton(self.buttons_frame) self.taskList_btn.setObjectName("taskList_btn") self.taskList_btn.setStyleSheet(self.style_button) self.taskList_btn.setMinimumHeight(25) - icon = QtGui.QIcon(self.iconspath+"check_2.png") + icon = QtGui.QIcon(resource_path('icons/check_2.png')) pixmap = icon.pixmap(QtCore.QSize(100, 100)) pixmap = pixmap.scaled(128, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) self.taskList_btn.setIcon(QtGui.QIcon(pixmap)) self.taskList_btn.setToolTip("Task List") - self.horizontalLayout.addWidget(self.taskList_btn) - self.verticalLayout.addLayout(self.horizontalLayout) - - # Add the input frame with buttons to the main layout - self.verticalLayout_2.addWidget(self.frame_input) + self.editButtonsHL.addWidget(self.taskList_btn) + + #========================================= adding input_frame to centralVL + self.centralVL.addWidget(self.buttons_frame) - # Create the edit area and add it to the splitter - self.splitter = QSplitter(Qt.Vertical) - self.editArea = QtWidgets.QTextEdit(self.centralwidget) + # ================= Spliter, InputArea(EditArea), tab_widget + # editArea is for a tab on tab_widget, added on call of function new_file() defined in main.py + self.tab_widget = QtWidgets.QTabWidget() + self.tab_widget.setTabsClosable(True) + self.splitter = QSplitter(QtCore.Qt.Vertical) + self.editArea = QtWidgets.QTextEdit() #TextEditWithLineNumbers() self.editArea.setStyleSheet("background-color: #DCDCDC;") self.editArea.setObjectName("editArea") font = QFont() font.setPointSize(16) self.editArea.setFont(font) - self.splitter.addWidget(self.editArea) - - # ================================ Layout que segura o splitter - self.verticalLayout_2.addWidget(self.splitter) + self.splitter.addWidget(self.tab_widget) + #========================================= adding splitter to centralVL + self.centralVL.addWidget(self.splitter) + + self.previewArea = QWebEngineView() + self.previewArea.setContextMenuPolicy(0) # Desabilita menu de contexto. 0=Qt.NoContextMenu + #========================================= adding previewArea to splitter + self.splitter.addWidget(self.previewArea) + MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 539, 22)) - self.menubar.setStyleSheet("background-color: #dfe2e5;") - self.menubar.setObjectName("menubar") - MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") - self.statusbar.setStyleSheet("background-color: #dfe2e5;") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) - # =============================================== conectando funcionalides dos botoes de ediçao de sintaxe Markdown + # Conecta ações auxilio markdown aos respectivos botoes self.heading_btn.clicked.connect(self.addHeader) self.bold_btn.clicked.connect(self.addBold) self.italic_btn.clicked.connect(self.addItalic) @@ -192,13 +311,44 @@ def setupUi(self, MainWindow): self.nList_btn.clicked.connect(self.addNList) self.taskList_btn.clicked.connect(self.addTaskList) - def statusBarMessage(self, texto:str): - label = QLabel(texto) - label.setAlignment(Qt.AlignCenter) # Centralizar o texto no rótulo + + def statusBarMessage(self): + self.init_status_bar() + def init_status_bar(self): - # Adicionar o rótulo à barra de status - self.statusbar.addWidget(label) + # Adicionando um rótulo à barra de status + # self.status_label = QLabel("\t\t Bem Vindo(a)!!") + # self.status_label.setStyleSheet("color: black;") + # self.statusbar.addWidget(self.status_label) # Use addWidget para garantir visibilidade + # Personalizando a barra de status com estilo + self.statusbar.setStyleSheet(""" + QStatusBar {{ + background-color: #2b2b2b; + color: black; + font-size: 12px; + }} + QLabel {{ + font-weight: bold; + color: black; + }} + QProgressBar {{ + border: 1px solid #3a3f44; + background-color: #1c1c1c; + color: #d4d4d4; + text-align: center; + }} + QPushButton {{ + border: 1px solid #5a5a5a; + padding: 3px; + border-radius: 3px; + }} + QPushButton:hover {{ + background-color: #4a4a4a; + }} + """) + + #================Funções para os botoes de auxilio do Markdown def update_font_size(self): font = self.editArea.font() font.setPointSize(self.fontSize_sp.value()) @@ -208,10 +358,7 @@ def update_font_style(self): font = self.editArea.font() font.setFamily(self.fontStyle_cb.currentText()) self.editArea.setFont(font) - - #> - # Funcionalidades Botões de edição de texto - # <# + def addHeader(self): cursor = self.editArea.textCursor() selected_text = cursor.selectedText() @@ -315,7 +462,8 @@ def addTaskList(self): cursor.insertText("- [ ] ") self.editArea.setTextCursor(cursor) self.editArea.setFocus() - + + #================ Função que traduz a UI def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "DocViewer")) diff --git a/MarkViewDesktop/welcome.md b/MarkViewDesktop/welcome.md new file mode 100644 index 0000000..12b1e0f --- /dev/null +++ b/MarkViewDesktop/welcome.md @@ -0,0 +1,8 @@ +# Bem vindo ao DocViewer +- Local onde voce pode facilmente escrever documentações em **_Markdown_**. Com auxilo de templates, vizualização em tempo real e uma documentação da sintaxe sempre á disposição para te auxiliar. + +### Domine a arte de documentar +- Escreva documentação concisas e faceis de entender com o auxilo de nosso templates você tera um guia de como começar a profissionalizar sua documentação. + +### Defina seu próprios templates de documentação +- Salve os modelos de documentação que você mais gosta ou aquele padrão chato que sua empresa exige. Desta maneira seu processo de documentar será muito mais eficiente e ágil. \ No newline at end of file