seems to be working quite great

This commit is contained in:
2024-10-24 19:09:51 +02:00
parent ed450424a5
commit 871cb4e08a

View File

@@ -205,29 +205,48 @@ class FileModel:
return lines return lines
def get_selection(self, byte_start: int, byte_end: int):
with FileWithTimeout.open(self.file, 5, 'rb') as f:
start = min(byte_start, byte_end)
end = max(byte_start, byte_end)
f.seek(start)
b = f.read(end - start)
# print(f"read {end - start } bytes -> {b}")
return b.decode("utf-8", errors="replace")
class Selection:
def __init__(self, byte_start: int, byte_end: int, text_before_selection: str, selected_text: str): class SelectionPos:
self.byte_start = byte_start def __init__(self, index: int, is_in_left_half: bool, num_bytes_of_char: int):
self.byte_end = byte_end self.index = index
self.text_before_selection = text_before_selection self.is_in_left_half = is_in_left_half
self.selected_text = selected_text self.num_bytes_of_char = num_bytes_of_char
def __repr__(self): def __repr__(self):
return f"{self.byte_start}:{self.byte_end} ({self.text_before_selection}:{self.selected_text})" return f"{self.index}{'🞀' if self.is_in_left_half else '🞂'}({self.num_bytes_of_char})"
def pos(self):
return self.index + (0 if self.is_in_left_half else self.num_bytes_of_char)
class Selection:
def __init__(self, start: SelectionPos = SelectionPos(0, False, 0), end: SelectionPos = SelectionPos(0, False, 0)):
self.start = start
self.end = end
def __repr__(self):
return f"{self.start}:{self.end}"
def min_byte(self) -> int: def min_byte(self) -> int:
return self.byte_start if self.byte_start < self.byte_end else self.byte_end return min(self.start.pos(), self.end.pos())
def max_byte(self) -> int: def max_byte(self) -> int:
return self.byte_end if self.byte_start < self.byte_end else self.byte_start return max(self.start.pos(), self.end.pos())
class BiggerText(QWidget): class BiggerText(QWidget):
def __init__(self, ): def __init__(self, ):
super(BiggerText, self).__init__() super(BiggerText, self).__init__()
self.selection = Selection(0, 0, "", "") self.selection = Selection()
self.mouse_pressed = False
self._encoding = "utf8" self._encoding = "utf8"
self.file_model: FileModel = FileModel("testdata/testset.txt") self.file_model: FileModel = FileModel("testdata/testset.txt")
@@ -246,25 +265,57 @@ class BiggerText(QWidget):
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
if e.buttons() == Qt.MouseButton.LeftButton and e.modifiers() == Qt.KeyboardModifier.NoModifier: if e.buttons() == Qt.MouseButton.LeftButton and e.modifiers() == Qt.KeyboardModifier.NoModifier:
(byte_offset, char_in_line, selected_text) = self.to_byte_offset(e.position()) self.selection.start = self.to_byte_offset(e.position())
self.selection.byte_start = byte_offset self.selection.end = self.selection.start
self.selection.byte_end = byte_offset + 1
self.selection.selected_text = selected_text
self.mouse_pressed = True self.mouse_pressed = True
return self.update()
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
self.mouse_pressed = False self.mouse_pressed = False
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
if self.mouse_pressed: if self.mouse_pressed:
(byte_offset, char_in_line, text_before_selection) = self.to_byte_offset(event.position()) self.selection.end = self.to_byte_offset(event.position())
self.selection.byte_end = byte_offset + 1 #print(f"selection: {self.selection} -> {self.file_model.get_selection(self.selection.min_byte(), self.selection.max_byte())}")
self.selection.text_before_selection = text_before_selection
print(f"selection: {self.selection}")
self.update() self.update()
def to_byte_offset(self, pos: QPoint) -> (int, int, str): def to_byte_offset(self, pos: QPoint) -> SelectionPos:
line_number = self.y_pos_to_line_number_on_screen(pos.y())
line = self.lines_to_render[line_number]
text: str = line.text()
text = text.replace("\n", "").replace("\r", "")
elided_text = self.elided_text(text, pos.x())
byte_offset = line.byte_offset() + len(elided_text.encode("utf8"))
left_x_offset = self.font_metric.horizontalAdvance(elided_text)
next_char = ""
pos_is_in_left_half = False
bytes_of_char = 0
if len(text) > len(elided_text): # has another character
next_char = text[len(elided_text)]
char_with = self.font_metric.horizontalAdvance(next_char)
pos_is_in_left_half = pos.x() < (left_x_offset + char_with / 2)
bytes_of_char = len(next_char.encode("utf8"))
else:
# the position is after the last character / behind the end of the line
pass
print(
f"to_byte_offset({pos.x()}, {pos.y()}) -> {left_x_offset} -- elided_text '{elided_text}' next_char '{next_char}' -> byte_offset {byte_offset} pos_is_in_left_half: {pos_is_in_left_half}")
return SelectionPos(byte_offset, pos_is_in_left_half, bytes_of_char)
def elided_text(self, text: str, width: int):
w = width + self.font_metric.horizontalAdvance("")
elided_text = self.font_metric.elidedText(text + "", Qt.TextElideMode.ElideRight, w,
Qt.TextFlag.TextWrapAnywhere)
elided_text = elided_text[0:-1] if elided_text.endswith('') else elided_text # remove the trailing '…'
return elided_text
def to_byte_offset_old(self, pos: QPoint) -> (int, int, str):
line_number = self.y_pos_to_line_number_on_screen(pos.y()) line_number = self.y_pos_to_line_number_on_screen(pos.y())
w = self.font_metric.horizontalAdvanceChar('') w = self.font_metric.horizontalAdvanceChar('')
@@ -286,15 +337,15 @@ class BiggerText(QWidget):
Qt.TextFlag.TextWrapAnywhere) Qt.TextFlag.TextWrapAnywhere)
elidedText = elidedText[0:-1] if elidedText.endswith('') else elidedText # remove the trailing '…' elidedText = elidedText[0:-1] if elidedText.endswith('') else elidedText # remove the trailing '…'
bounding_box_elided = font_metric.boundingRect(elidedText) bounding_box_elided = font_metric.horizontalAdvance(elidedText)
if len(elidedText) + 1 < len(text): # there is a next character if len(elidedText) + 1 < len(text): # there is a next character
elidet_text_plus_one_char = text[0:len(elidedText) + 1] elidet_text_plus_one_char = text[0:len(elidedText) + 1]
print(f"elidet_text_plus_one_char: {elidet_text_plus_one_char}") # print(f"elidet_text_plus_one_char: {elidet_text_plus_one_char}")
bounding_box_elided_plus_one = font_metric.boundingRect(elidet_text_plus_one_char) bounding_box_elided_plus_one = font_metric.horizontalAdvance(elidet_text_plus_one_char)
diff = bounding_box_elided_plus_one.width() - bounding_box_elided.width() diff = bounding_box_elided_plus_one - bounding_box_elided.width()
if bounding_box_elided.width() + diff / 2 >= x: if bounding_box_elided + diff / 2 >= x:
return elidet_text_plus_one_char return elidet_text_plus_one_char
else: else:
return elidedText return elidedText
@@ -313,7 +364,7 @@ class BiggerText(QWidget):
# --- # ---
painter.drawLine(QLine(273, 0, 273, self.height())) #painter.drawLine(QLine(273, 0, 273, self.height()))
# --- # ---
# ---- # ----
@@ -325,25 +376,60 @@ class BiggerText(QWidget):
self.lines_to_render: [Line] = self.file_model.read(0, lines_to_read, 200, self._encoding) self.lines_to_render: [Line] = self.file_model.read(0, lines_to_read, 200, self._encoding)
painter.setPen(QColor(0, 0, 0)) painter.setPen(QColor(0, 0, 0))
line_on_screen = 0
for line in self.lines_to_render: if False:
text = line.text() line_on_screen = 0
text = text.replace("\n", "").replace("\r", "") for line in self.lines_to_render:
r = self.font_metric.boundingRect(text[0:1]) text = line.text()
text = text.replace("\n", "").replace("\r", "")
w = self.font_metric.horizontalAdvanceChar('') start = 0
end = 1
xpos = r.x() + r.width() + w + 1 if start < len(text) and end <= len(text):
x_start = self.font_metric.horizontalAdvance(text[0:start])
# x_width = self.font_metric.horizontalAdvance(text[start:end])
x_width = self.font_metric.boundingRect(text[start:end]).width()
elidet_text = self.font_metric.elidedText(text, Qt.TextElideMode.ElideNone, 50,
Qt.TextFlag.TextWrapAnywhere)
# x_width = self.font_metric.boundingRect(elidet_text).width()
print(f"-> {text[start:end]} width: {x_width} elidet_text: {elidet_text}")
elidet_text = self.font_metric.elidedText(text, Qt.TextElideMode.ElideRight, xpos) y = int(line_on_screen * self.char_height)
painter.setBrush(QBrush(QColor(233, 133, 233)))
painter.drawRect(QRect(x_start, y, x_width, self.char_height))
line_on_screen = line_on_screen + 1
print(f"-> {elidet_text} -- {text}") if False:
line_on_screen = 0
for line in self.lines_to_render:
text = line.text()
text = text.replace("\n", "").replace("\r", "")
r = self.font_metric.boundingRect(text[0:1])
y = int(line_on_screen * self.char_height) w = self.font_metric.horizontalAdvanceChar('')
painter.setBrush(QBrush(QColor(233, 233, 233)))
painter.drawRect(QRect(0, y, r.x() + r.width(), self.char_height)) xpos = r.x() + r.width() + w + 1
line_on_screen = line_on_screen + 1
elidet_text = self.font_metric.elidedText(text, Qt.TextElideMode.ElideRight, xpos)
print(f"-> {elidet_text} -- {text}")
y = int(line_on_screen * self.char_height)
painter.setBrush(QBrush(QColor(233, 233, 233)))
painter.drawRect(QRect(0, y, r.x() + r.width(), self.char_height))
line_on_screen = line_on_screen + 1
if False:
line_on_screen = 0
for line in self.lines_to_render:
text = line.text()
text = text.replace("\n", "").replace("\r", "")
x = self.font_metric.horizontalAdvance(text)
y = int(line_on_screen * self.char_height)
painter.setBrush(QBrush(QColor(233, 233, 233)))
painter.drawRect(QRect(0, y, x, self.char_height))
line_on_screen = line_on_screen + 1
line_on_screen = 1 line_on_screen = 1
for line in self.lines_to_render: for line in self.lines_to_render:
@@ -358,7 +444,10 @@ class BiggerText(QWidget):
if line.byte_offset() <= self.selection.min_byte() <= line.byte_end(): if line.byte_offset() <= self.selection.min_byte() <= line.byte_end():
left_offset_in_bytes = self.selection.min_byte() - line.byte_offset() left_offset_in_bytes = self.selection.min_byte() - line.byte_offset()
bytes = line.bytes()[0:left_offset_in_bytes] bytes = line.bytes()[0:left_offset_in_bytes]
x_start = self.font_metric.boundingRect(bytes.decode(self._encoding, errors="replace")).width() chars = bytes.decode(self._encoding, errors="replace")
x_start = self.font_metric.horizontalAdvance(chars)
#print(f"width({chars}) -> bounding_rect={self.font_metric.boundingRect(chars).width()}px or horizontalAdvance={self.font_metric.horizontalAdvance(chars)}")
# selection ends after line # selection ends after line
if self.selection.max_byte() > line.byte_end(): if self.selection.max_byte() > line.byte_end():
@@ -368,10 +457,10 @@ class BiggerText(QWidget):
if line.byte_offset() <= self.selection.max_byte() <= line.byte_end(): if line.byte_offset() <= self.selection.max_byte() <= line.byte_end():
left_offset_in_bytes = self.selection.max_byte() - line.byte_offset() left_offset_in_bytes = self.selection.max_byte() - line.byte_offset()
bytes = line.bytes()[0:left_offset_in_bytes] bytes = line.bytes()[0:left_offset_in_bytes]
x_end = self.font_metric.boundingRect(bytes.decode(self._encoding, errors="replace")).width() - x_start x_end = self.font_metric.horizontalAdvance(bytes.decode(self._encoding, errors="replace")) - x_start
if x_start >= 0 and x_end >= 0: if x_start >= 0 and x_end >= 0:
print(f"highlighting in line {line_on_screen} -- x_start: {x_start} -> x_end: {x_end}") #print(f"highlighting in line {line_on_screen} -- x_start: {x_start} -> x_end: {x_end}")
prev_brush = painter.brush() prev_brush = painter.brush()
prev_pen = painter.pen() prev_pen = painter.pen()
painter.setBrush(QBrush(QColor(0, 255, 255))) painter.setBrush(QBrush(QColor(0, 255, 255)))