mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Start work on Live CSS view
This commit is contained in:
parent
06f3640ceb
commit
905e1b5e86
@ -228,6 +228,7 @@ class Boss(QObject):
|
|||||||
for name in tuple(editors):
|
for name in tuple(editors):
|
||||||
self.close_editor(name)
|
self.close_editor(name)
|
||||||
self.gui.preview.clear()
|
self.gui.preview.clear()
|
||||||
|
self.gui.live_css.clear()
|
||||||
self.container_count = -1
|
self.container_count = -1
|
||||||
if self.tdir:
|
if self.tdir:
|
||||||
shutil.rmtree(self.tdir, ignore_errors=True)
|
shutil.rmtree(self.tdir, ignore_errors=True)
|
||||||
@ -311,6 +312,7 @@ class Boss(QObject):
|
|||||||
self.close_editor(name)
|
self.close_editor(name)
|
||||||
if not editors:
|
if not editors:
|
||||||
self.gui.preview.clear()
|
self.gui.preview.clear()
|
||||||
|
self.gui.live_css.clear()
|
||||||
if remove_names_from_toc(current_container(), spine_names + list(other_items)):
|
if remove_names_from_toc(current_container(), spine_names + list(other_items)):
|
||||||
self.gui.toc_view.update_if_visible()
|
self.gui.toc_view.update_if_visible()
|
||||||
toc = find_existing_toc(current_container())
|
toc = find_existing_toc(current_container())
|
||||||
@ -1028,11 +1030,19 @@ class Boss(QObject):
|
|||||||
if name is not None and getattr(ed, 'syntax', None) == 'html':
|
if name is not None and getattr(ed, 'syntax', None) == 'html':
|
||||||
self.gui.preview.sync_to_editor(name, ed.current_line)
|
self.gui.preview.sync_to_editor(name, ed.current_line)
|
||||||
|
|
||||||
|
def sync_live_css_to_editor(self):
|
||||||
|
ed = self.gui.central.current_editor
|
||||||
|
if ed is not None:
|
||||||
|
name = editor_name(ed)
|
||||||
|
if name is not None and getattr(ed, 'syntax', None) == 'html':
|
||||||
|
self.gui.live_css.sync_to_editor(name)
|
||||||
|
|
||||||
def init_editor(self, name, editor, data=None, use_template=False):
|
def init_editor(self, name, editor, data=None, use_template=False):
|
||||||
editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed)
|
editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed)
|
||||||
editor.data_changed.connect(self.editor_data_changed)
|
editor.data_changed.connect(self.editor_data_changed)
|
||||||
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
editor.copy_available_state_changed.connect(self.editor_copy_available_state_changed)
|
||||||
editor.cursor_position_changed.connect(self.sync_preview_to_editor)
|
editor.cursor_position_changed.connect(self.sync_preview_to_editor)
|
||||||
|
editor.cursor_position_changed.connect(self.sync_live_css_to_editor)
|
||||||
editor.cursor_position_changed.connect(self.update_cursor_position)
|
editor.cursor_position_changed.connect(self.update_cursor_position)
|
||||||
if hasattr(editor, 'word_ignored'):
|
if hasattr(editor, 'word_ignored'):
|
||||||
editor.word_ignored.connect(self.word_ignored)
|
editor.word_ignored.connect(self.word_ignored)
|
||||||
@ -1153,6 +1163,7 @@ class Boss(QObject):
|
|||||||
# focused. This is not inefficient since multiple requests
|
# focused. This is not inefficient since multiple requests
|
||||||
# to sync are de-bounced with a 100 msec wait.
|
# to sync are de-bounced with a 100 msec wait.
|
||||||
self.sync_preview_to_editor()
|
self.sync_preview_to_editor()
|
||||||
|
self.sync_live_css_to_editor()
|
||||||
if name is not None:
|
if name is not None:
|
||||||
self.gui.file_list.mark_name_as_current(name)
|
self.gui.file_list.mark_name_as_current(name)
|
||||||
if ed.has_line_numbers:
|
if ed.has_line_numbers:
|
||||||
@ -1182,6 +1193,7 @@ class Boss(QObject):
|
|||||||
editor.break_cycles()
|
editor.break_cycles()
|
||||||
if not editors or getattr(self.gui.central.current_editor, 'syntax', None) != 'html':
|
if not editors or getattr(self.gui.central.current_editor, 'syntax', None) != 'html':
|
||||||
self.gui.preview.clear()
|
self.gui.preview.clear()
|
||||||
|
self.gui.live_css.clear()
|
||||||
|
|
||||||
def insert_character(self):
|
def insert_character(self):
|
||||||
self.gui.insert_char.show()
|
self.gui.insert_char.show()
|
||||||
@ -1251,6 +1263,7 @@ class Boss(QObject):
|
|||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.gui.preview.stop_refresh_timer()
|
self.gui.preview.stop_refresh_timer()
|
||||||
|
self.gui.live_css.stop_update_timer()
|
||||||
self.save_state()
|
self.save_state()
|
||||||
[x.reject() for x in _diff_dialogs]
|
[x.reject() for x in _diff_dialogs]
|
||||||
del _diff_dialogs[:]
|
del _diff_dialogs[:]
|
||||||
|
@ -20,3 +20,6 @@ class NullSmarts(object):
|
|||||||
def verify_for_spellcheck(self, cursor, highlighter):
|
def verify_for_spellcheck(self, cursor, highlighter):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def cursor_position_with_sourceline(self, cursor):
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
@ -312,3 +312,26 @@ class HTMLSmarts(NullSmarts):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def cursor_position_with_sourceline(self, cursor):
|
||||||
|
''' Return the tag containing the current cursor as a source line
|
||||||
|
number and a list of tags defined on that line upto and including the
|
||||||
|
containing tag. '''
|
||||||
|
block = cursor.block()
|
||||||
|
offset = cursor.position() - block.position()
|
||||||
|
block, boundary = next_tag_boundary(block, offset, forward=False)
|
||||||
|
if block is None:
|
||||||
|
return None, None
|
||||||
|
if boundary.is_start:
|
||||||
|
# We are inside a tag, use this tag
|
||||||
|
start_block, start_offset = block, boundary.offset
|
||||||
|
else:
|
||||||
|
tag = find_closest_containing_tag(block, offset)
|
||||||
|
if tag is None:
|
||||||
|
return None, None
|
||||||
|
start_block, start_offset = tag.start_block, tag.start_offset
|
||||||
|
sourceline = start_block.blockNumber()
|
||||||
|
ud = start_block.userData()
|
||||||
|
if ud is None:
|
||||||
|
return None, None
|
||||||
|
all_tags = [t.name for t in ud.tags if (t.is_start and not t.closing and t.offset <= start_offset)]
|
||||||
|
return sourceline, all_tags
|
||||||
|
@ -749,3 +749,6 @@ class TextEdit(PlainTextEdit):
|
|||||||
if hasattr(self.smarts, 'rename_block_tag'):
|
if hasattr(self.smarts, 'rename_block_tag'):
|
||||||
self.smarts.rename_block_tag(self, new_name)
|
self.smarts.rename_block_tag(self, new_name)
|
||||||
|
|
||||||
|
def current_tag(self):
|
||||||
|
return self.smarts.cursor_position_with_sourceline(self.textCursor())
|
||||||
|
|
||||||
|
@ -112,6 +112,9 @@ class Editor(QMainWindow):
|
|||||||
self.editor.go_to_line(val)
|
self.editor.go_to_line(val)
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def current_tag(self):
|
||||||
|
return self.editor.current_tag()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number_of_lines(self):
|
def number_of_lines(self):
|
||||||
return self.editor.blockCount()
|
return self.editor.blockCount()
|
||||||
|
65
src/calibre/gui2/tweak_book/live_css.py
Normal file
65
src/calibre/gui2/tweak_book/live_css.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QWidget, QTimer)
|
||||||
|
|
||||||
|
from calibre.gui2.tweak_book import editors, actions
|
||||||
|
|
||||||
|
class LiveCSS(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, preview, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.preview = preview
|
||||||
|
preview.refreshed.connect(self.update_data)
|
||||||
|
self.update_timer = QTimer(self)
|
||||||
|
self.update_timer.timeout.connect(self.update_data)
|
||||||
|
self.update_timer.setSingleShot(True)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
pass # TODO: Implement this
|
||||||
|
|
||||||
|
def show_data(self, editor_name, sourceline, tags):
|
||||||
|
if sourceline is None:
|
||||||
|
self.clear()
|
||||||
|
else:
|
||||||
|
pass # TODO: Do update
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_name(self):
|
||||||
|
return self.preview.current_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_visible(self):
|
||||||
|
return self.isVisible()
|
||||||
|
|
||||||
|
def showEvent(self, ev):
|
||||||
|
self.update_timer.start()
|
||||||
|
actions['auto-reload-preview'].setEnabled(True)
|
||||||
|
return QWidget.showEvent(self, ev)
|
||||||
|
|
||||||
|
def sync_to_editor(self, name):
|
||||||
|
self.start_update_timer()
|
||||||
|
|
||||||
|
def update_data(self):
|
||||||
|
if not self.is_visible:
|
||||||
|
return
|
||||||
|
editor_name = self.current_name
|
||||||
|
ed = editors.get(editor_name, None)
|
||||||
|
if self.update_timer.isActive() or (ed is None and editor_name is not None):
|
||||||
|
return QTimer.singleShot(100, self.update_data)
|
||||||
|
if ed is not None:
|
||||||
|
sourceline, tags = ed.current_tag()
|
||||||
|
self.show_data(editor_name, sourceline, tags)
|
||||||
|
|
||||||
|
def start_update_timer(self):
|
||||||
|
if self.is_visible:
|
||||||
|
self.update_timer.start(1000)
|
||||||
|
|
||||||
|
def stop_update_timer(self):
|
||||||
|
self.update_timer.stop()
|
||||||
|
|
@ -435,6 +435,7 @@ class Preview(QWidget):
|
|||||||
split_requested = pyqtSignal(object, object, object)
|
split_requested = pyqtSignal(object, object, object)
|
||||||
split_start_requested = pyqtSignal()
|
split_start_requested = pyqtSignal()
|
||||||
link_clicked = pyqtSignal(object, object)
|
link_clicked = pyqtSignal(object, object)
|
||||||
|
refreshed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -558,6 +559,7 @@ class Preview(QWidget):
|
|||||||
self.view.setUrl(current_url)
|
self.view.setUrl(current_url)
|
||||||
else:
|
else:
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
self.refreshed.emit()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.view.clear()
|
self.view.clear()
|
||||||
@ -567,14 +569,26 @@ class Preview(QWidget):
|
|||||||
def is_visible(self):
|
def is_visible(self):
|
||||||
return actions['preview-dock'].isChecked()
|
return actions['preview-dock'].isChecked()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def live_css_is_visible(self):
|
||||||
|
try:
|
||||||
|
return actions['live-css-dock'].isChecked()
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
def start_refresh_timer(self):
|
def start_refresh_timer(self):
|
||||||
if self.is_visible and actions['auto-reload-preview'].isChecked():
|
if self.live_css_is_visible or (self.is_visible and actions['auto-reload-preview'].isChecked()):
|
||||||
self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000)
|
self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000)
|
||||||
|
|
||||||
def stop_refresh_timer(self):
|
def stop_refresh_timer(self):
|
||||||
self.refresh_timer.stop()
|
self.refresh_timer.stop()
|
||||||
|
|
||||||
def auto_reload_toggled(self, checked):
|
def auto_reload_toggled(self, checked):
|
||||||
|
if self.live_css_is_visible and not actions['auto-reload-preview'].isChecked():
|
||||||
|
actions['auto-reload-preview'].setChecked(True)
|
||||||
|
error_dialog(self, _('Cannot disable'), _(
|
||||||
|
'Auto reloading of the preview panel cannot be disabled while the'
|
||||||
|
' Live CSS panel is open.'), show=True)
|
||||||
actions['auto-reload-preview'].setToolTip(_(
|
actions['auto-reload-preview'].setToolTip(_(
|
||||||
'Auto reload preview when text changes in editor') if not checked else _(
|
'Auto reload preview when text changes in editor') if not checked else _(
|
||||||
'Disable auto reload of preview'))
|
'Disable auto reload of preview'))
|
||||||
|
@ -33,6 +33,7 @@ from calibre.gui2.tweak_book.spell import SpellCheck
|
|||||||
from calibre.gui2.tweak_book.search import SavedSearches
|
from calibre.gui2.tweak_book.search import SavedSearches
|
||||||
from calibre.gui2.tweak_book.toc import TOCViewer
|
from calibre.gui2.tweak_book.toc import TOCViewer
|
||||||
from calibre.gui2.tweak_book.char_select import CharSelect
|
from calibre.gui2.tweak_book.char_select import CharSelect
|
||||||
|
from calibre.gui2.tweak_book.live_css import LiveCSS
|
||||||
from calibre.gui2.tweak_book.editor.widget import register_text_editor_actions
|
from calibre.gui2.tweak_book.editor.widget import register_text_editor_actions
|
||||||
from calibre.gui2.tweak_book.editor.insert_resource import InsertImage
|
from calibre.gui2.tweak_book.editor.insert_resource import InsertImage
|
||||||
from calibre.utils.icu import character_name
|
from calibre.utils.icu import character_name
|
||||||
@ -595,6 +596,13 @@ class Main(MainWindow):
|
|||||||
d.setWidget(self.preview)
|
d.setWidget(self.preview)
|
||||||
self.addDockWidget(Qt.RightDockWidgetArea, d)
|
self.addDockWidget(Qt.RightDockWidgetArea, d)
|
||||||
|
|
||||||
|
d = create(_('Live CSS'), 'live-css')
|
||||||
|
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
||||||
|
self.live_css = LiveCSS(self.preview, parent=d)
|
||||||
|
d.setWidget(self.live_css)
|
||||||
|
self.addDockWidget(Qt.RightDockWidgetArea, d)
|
||||||
|
d.close() # Hidden by default
|
||||||
|
|
||||||
d = create(_('Check Book'), 'check-book')
|
d = create(_('Check Book'), 'check-book')
|
||||||
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
|
||||||
d.setWidget(self.check_book)
|
d.setWidget(self.check_book)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user