pygpt-net 2.4.37__py3-none-any.whl → 2.4.39__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +11 -0
- README.md +24 -5
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/__init__.py +3 -1
- pygpt_net/controller/calendar/__init__.py +3 -1
- pygpt_net/controller/chat/render.py +8 -5
- pygpt_net/controller/config/placeholder.py +29 -0
- pygpt_net/controller/ctx/__init__.py +32 -24
- pygpt_net/controller/ctx/common.py +3 -2
- pygpt_net/controller/dialogs/confirm.py +2 -2
- pygpt_net/controller/lang/custom.py +2 -7
- pygpt_net/controller/lang/mapping.py +2 -2
- pygpt_net/controller/layout.py +2 -2
- pygpt_net/controller/notepad.py +8 -5
- pygpt_net/controller/settings/editor.py +6 -0
- pygpt_net/controller/theme/__init__.py +33 -8
- pygpt_net/controller/theme/common.py +22 -1
- pygpt_net/controller/theme/markdown.py +26 -14
- pygpt_net/controller/theme/menu.py +26 -5
- pygpt_net/controller/ui/tabs.py +201 -58
- pygpt_net/core/attachments/context.py +14 -12
- pygpt_net/core/audio/__init__.py +59 -1
- pygpt_net/core/ctx/__init__.py +11 -1
- pygpt_net/core/ctx/container.py +16 -9
- pygpt_net/core/ctx/output.py +86 -67
- pygpt_net/core/debug/tabs.py +3 -2
- pygpt_net/core/filesystem/__init__.py +5 -19
- pygpt_net/core/filesystem/url.py +7 -3
- pygpt_net/core/render/base.py +14 -3
- pygpt_net/core/render/markdown/renderer.py +3 -1
- pygpt_net/core/render/plain/renderer.py +3 -3
- pygpt_net/core/render/web/body.py +39 -17
- pygpt_net/core/render/web/renderer.py +7 -5
- pygpt_net/core/tabs/__init__.py +180 -71
- pygpt_net/core/tabs/tab.py +13 -4
- pygpt_net/data/config/config.json +14 -4
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/modes.json +3 -3
- pygpt_net/data/config/settings.json +55 -10
- pygpt_net/data/config/settings_section.json +3 -0
- pygpt_net/data/css/style.light.css +1 -0
- pygpt_net/data/css/{web.css → web-blocks.css} +144 -133
- pygpt_net/data/css/web-chatgpt.css +342 -0
- pygpt_net/data/css/web-chatgpt.dark.css +64 -0
- pygpt_net/data/css/web-chatgpt.light.css +75 -0
- pygpt_net/data/css/web-chatgpt_wide.css +342 -0
- pygpt_net/data/css/web-chatgpt_wide.dark.css +64 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +75 -0
- pygpt_net/data/locale/locale.de.ini +12 -0
- pygpt_net/data/locale/locale.en.ini +14 -1
- pygpt_net/data/locale/locale.es.ini +12 -0
- pygpt_net/data/locale/locale.fr.ini +12 -0
- pygpt_net/data/locale/locale.it.ini +12 -0
- pygpt_net/data/locale/locale.pl.ini +12 -0
- pygpt_net/data/locale/locale.uk.ini +12 -0
- pygpt_net/data/locale/locale.zh.ini +12 -0
- pygpt_net/plugin/audio_input/simple.py +21 -5
- pygpt_net/provider/core/config/patch.py +22 -1
- pygpt_net/provider/core/ctx/base.py +4 -1
- pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
- pygpt_net/ui/layout/chat/input.py +10 -18
- pygpt_net/ui/layout/chat/output.py +26 -44
- pygpt_net/ui/layout/toolbox/footer.py +18 -2
- pygpt_net/ui/menu/config.py +7 -11
- pygpt_net/ui/menu/theme.py +9 -2
- pygpt_net/ui/widget/lists/context.py +1 -0
- pygpt_net/ui/widget/tabs/layout.py +195 -0
- pygpt_net/ui/widget/tabs/output.py +124 -35
- pygpt_net/ui/widget/textarea/html.py +11 -1
- pygpt_net/ui/widget/textarea/output.py +10 -1
- pygpt_net/ui/widget/textarea/search_input.py +4 -1
- pygpt_net/ui/widget/textarea/web.py +49 -9
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.39.dist-info}/METADATA +25 -6
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.39.dist-info}/RECORD +81 -74
- /pygpt_net/data/css/{web.dark.css → web-blocks.dark.css} +0 -0
- /pygpt_net/data/css/{web.light.css → web-blocks.light.css} +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.39.dist-info}/LICENSE +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.39.dist-info}/WHEEL +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.39.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
7
7
|
# MIT License #
|
8
8
|
# Created By : Marcin Szczygliński #
|
9
|
-
# Updated Date: 2024.
|
9
|
+
# Updated Date: 2024.12.07 21:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
@@ -156,3 +156,24 @@ class Common:
|
|
156
156
|
with open(path) as file:
|
157
157
|
content += file.read()
|
158
158
|
return content
|
159
|
+
|
160
|
+
def get_styles_list(self) -> list:
|
161
|
+
"""
|
162
|
+
Return a list of available styles
|
163
|
+
|
164
|
+
:return: list of styles names
|
165
|
+
"""
|
166
|
+
styles = []
|
167
|
+
app_dir = os.path.join(self.window.core.config.get_app_path(), 'data', 'css')
|
168
|
+
user_dir = os.path.join(self.window.core.config.path, 'css')
|
169
|
+
for path in [app_dir, user_dir]:
|
170
|
+
if not os.path.exists(path):
|
171
|
+
continue
|
172
|
+
for file in os.listdir(path):
|
173
|
+
if file.startswith("web-") and file.endswith('.css'):
|
174
|
+
to_replace = ['web-', '.css', '.light', '.dark']
|
175
|
+
for item in to_replace:
|
176
|
+
file = file.replace(item, '')
|
177
|
+
if file not in styles:
|
178
|
+
styles.append(file)
|
179
|
+
return sorted(styles)
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
7
7
|
# MIT License #
|
8
8
|
# Created By : Marcin Szczygliński #
|
9
|
-
# Updated Date: 2024.
|
9
|
+
# Updated Date: 2024.12.07 21:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
@@ -23,6 +23,7 @@ class Markdown:
|
|
23
23
|
"""
|
24
24
|
self.window = window
|
25
25
|
self.css = {} # external styles
|
26
|
+
self.web_style = ""
|
26
27
|
|
27
28
|
def update(self, force: bool = False):
|
28
29
|
"""
|
@@ -60,7 +61,8 @@ class Markdown:
|
|
60
61
|
|
61
62
|
:return: stylesheet
|
62
63
|
"""
|
63
|
-
|
64
|
+
web_style = self.window.core.config.get("theme.style", "blocks")
|
65
|
+
if "web" not in self.css or self.web_style != web_style:
|
64
66
|
self.load()
|
65
67
|
if "web" in self.css:
|
66
68
|
return self.css["web"]
|
@@ -73,31 +75,41 @@ class Markdown:
|
|
73
75
|
self.window.dispatch(event) # per current engine
|
74
76
|
self.window.controller.ctx.refresh()
|
75
77
|
self.window.controller.ctx.refresh_output()
|
76
|
-
|
78
|
+
event = RenderEvent(RenderEvent.END, {
|
77
79
|
"meta": meta,
|
78
|
-
}
|
79
|
-
event = RenderEvent(RenderEvent.END, data)
|
80
|
+
})
|
80
81
|
self.window.dispatch(event)
|
81
82
|
|
82
83
|
def load(self):
|
83
84
|
"""Load markdown styles"""
|
84
|
-
parents = [
|
85
|
+
parents = [
|
86
|
+
"markdown",
|
87
|
+
"web",
|
88
|
+
]
|
89
|
+
web_style = self.window.core.config.get("theme.style", "blocks")
|
85
90
|
for base_name in parents:
|
91
|
+
suffix = ""
|
92
|
+
if base_name == 'web':
|
93
|
+
suffix = "-" + web_style
|
94
|
+
self.web_style = web_style
|
86
95
|
theme = self.window.core.config.get('theme')
|
87
96
|
name = str(base_name)
|
88
|
-
color_name = str(base_name)
|
89
97
|
if theme.startswith('light'):
|
90
|
-
|
98
|
+
color = '.light'
|
91
99
|
else:
|
92
|
-
|
100
|
+
color = '.dark'
|
101
|
+
|
102
|
+
# load CSS, app + user
|
103
|
+
file_base = name + suffix + '.css'
|
104
|
+
file_color = name + suffix + color + '.css'
|
93
105
|
paths = []
|
94
|
-
paths.append(os.path.join(self.window.core.config.get_app_path(), 'data', 'css',
|
95
|
-
paths.append(os.path.join(self.window.core.config.get_app_path(), 'data', 'css',
|
96
|
-
paths.append(os.path.join(self.window.core.config.get_user_path(), 'css',
|
97
|
-
paths.append(os.path.join(self.window.core.config.get_user_path(), 'css',
|
106
|
+
paths.append(os.path.join(self.window.core.config.get_app_path(), 'data', 'css', file_base))
|
107
|
+
paths.append(os.path.join(self.window.core.config.get_app_path(), 'data', 'css', file_color))
|
108
|
+
paths.append(os.path.join(self.window.core.config.get_user_path(), 'css', file_base))
|
109
|
+
paths.append(os.path.join(self.window.core.config.get_user_path(), 'css', file_color))
|
98
110
|
content = ''
|
99
111
|
for path in paths:
|
100
|
-
if os.path.exists(path):
|
112
|
+
if os.path.exists(path) and os.path.isfile(path):
|
101
113
|
with open(path, 'r') as file:
|
102
114
|
content += file.read()
|
103
115
|
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
7
7
|
# MIT License #
|
8
8
|
# Created By : Marcin Szczygliński #
|
9
|
-
# Updated Date: 2024.
|
9
|
+
# Updated Date: 2024.12.07 21:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from PySide6.QtGui import QAction
|
@@ -30,6 +30,18 @@ class Menu:
|
|
30
30
|
# setup themes list menu
|
31
31
|
if self.loaded:
|
32
32
|
return
|
33
|
+
|
34
|
+
# styles
|
35
|
+
styles = self.window.controller.theme.common.get_styles_list()
|
36
|
+
for style in styles:
|
37
|
+
style_id = style.lower()
|
38
|
+
title = style.replace('_', ' ').title()
|
39
|
+
self.window.ui.menu['theme_style'][style_id] = QAction(title, self.window, checkable=True)
|
40
|
+
self.window.ui.menu['theme_style'][style_id].triggered.connect(
|
41
|
+
lambda checked=None, style=style_id: self.window.controller.theme.toggle_style(style))
|
42
|
+
self.window.ui.menu['theme.style'].addAction(self.window.ui.menu['theme_style'][style_id])
|
43
|
+
|
44
|
+
# color themes
|
33
45
|
themes = self.window.controller.theme.common.get_themes_list()
|
34
46
|
for theme in themes:
|
35
47
|
name = self.window.controller.theme.common.translate(theme)
|
@@ -42,6 +54,7 @@ class Menu:
|
|
42
54
|
self.window.ui.menu['theme.dark'].addAction(self.window.ui.menu['theme'][theme])
|
43
55
|
elif theme.startswith('light'):
|
44
56
|
self.window.ui.menu['theme.light'].addAction(self.window.ui.menu['theme'][theme])
|
57
|
+
|
45
58
|
self.loaded = True
|
46
59
|
|
47
60
|
def setup_syntax(self):
|
@@ -86,14 +99,22 @@ class Menu:
|
|
86
99
|
|
87
100
|
def update_list(self):
|
88
101
|
"""Update theme list menu"""
|
89
|
-
|
102
|
+
# styles
|
103
|
+
current_style = self.window.core.config.get('theme.style')
|
104
|
+
for style in self.window.ui.menu['theme_style']:
|
105
|
+
self.window.ui.menu['theme_style'][style].setChecked(False)
|
106
|
+
if current_style in self.window.ui.menu['theme_style']:
|
107
|
+
self.window.ui.menu['theme_style'][current_style].setChecked(True)
|
108
|
+
|
109
|
+
# color themes
|
110
|
+
current_theme = self.window.core.config.get('theme')
|
90
111
|
for theme in self.window.ui.menu['theme']:
|
91
112
|
self.window.ui.menu['theme'][theme].setChecked(False)
|
92
|
-
if
|
93
|
-
self.window.ui.menu['theme'][
|
113
|
+
if current_theme in self.window.ui.menu['theme']:
|
114
|
+
self.window.ui.menu['theme'][current_theme].setChecked(True)
|
94
115
|
|
95
116
|
def update_syntax(self):
|
96
|
-
"""Update syntax menu"""
|
117
|
+
"""Update code syntax highlight menu"""
|
97
118
|
current = self.window.core.config.get('render.code_syntax')
|
98
119
|
for style in self.window.ui.menu['theme_syntax']:
|
99
120
|
self.window.ui.menu['theme_syntax'][style].setChecked(False)
|
pygpt_net/controller/ui/tabs.py
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
7
7
|
# MIT License #
|
8
8
|
# Created By : Marcin Szczygliński #
|
9
|
-
# Updated Date: 2024.
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from PySide6.QtCore import QTimer
|
@@ -29,35 +29,66 @@ class Tabs:
|
|
29
29
|
self.initialized = False
|
30
30
|
self.appended = False
|
31
31
|
self.current = 0
|
32
|
+
self.column_idx = 0
|
33
|
+
self.tmp_column_idx = 0
|
34
|
+
self.locked = False
|
32
35
|
|
33
36
|
def setup(self):
|
34
37
|
"""Setup tabs"""
|
35
38
|
self.window.core.tabs.load()
|
36
39
|
self.window.controller.notepad.load()
|
40
|
+
self.setup_options()
|
37
41
|
self.initialized = True
|
38
42
|
|
39
|
-
def
|
43
|
+
def setup_options(self):
|
44
|
+
"""Setup options"""
|
45
|
+
state = self.window.core.config.get("layout.split", False)
|
46
|
+
self.window.ui.nodes['layout.split'].setChecked(state)
|
47
|
+
if not state:
|
48
|
+
self.window.ui.splitters['columns'].setSizes([1, 0])
|
49
|
+
|
50
|
+
def add(
|
51
|
+
self,
|
52
|
+
type: int,
|
53
|
+
title: str,
|
54
|
+
icon=None,
|
55
|
+
child=None,
|
56
|
+
data_id=None
|
57
|
+
):
|
40
58
|
"""
|
41
|
-
Add tab
|
59
|
+
Add a new tab
|
42
60
|
|
43
61
|
:param type: Tab type
|
44
62
|
:param title: Tab title
|
45
63
|
:param icon: Tab icon
|
46
|
-
:param
|
47
|
-
:param data_id: Tab data ID
|
48
|
-
"""
|
49
|
-
self.window.core.tabs.add(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
64
|
+
:param child: Tab child (child widget)
|
65
|
+
:param data_id: Tab data ID (child data ID)
|
66
|
+
"""
|
67
|
+
self.window.core.tabs.add(
|
68
|
+
type=type,
|
69
|
+
title=title,
|
70
|
+
icon=icon,
|
71
|
+
child=child,
|
72
|
+
data_id=data_id
|
73
|
+
)
|
74
|
+
|
75
|
+
def append(
|
76
|
+
self,
|
77
|
+
type: int,
|
78
|
+
idx: int,
|
79
|
+
column_idx: int = 0
|
80
|
+
):
|
81
|
+
"""
|
82
|
+
Append tab at tab index
|
54
83
|
|
55
84
|
:param type: Tab type
|
56
85
|
:param idx: Tab index
|
86
|
+
:param column_idx: Column index
|
57
87
|
"""
|
58
88
|
self.appended = True # lock reload in previous tab
|
59
|
-
self.
|
60
|
-
self.
|
89
|
+
self.column_idx = column_idx # switch to column
|
90
|
+
self.window.core.tabs.append(type, idx, column_idx)
|
91
|
+
self.switch_tab_by_idx(idx + 1, column_idx) # switch to new tab
|
61
92
|
|
62
93
|
def reload_titles(self):
|
63
94
|
"""Reload tab titles"""
|
@@ -78,15 +109,16 @@ class Tabs:
|
|
78
109
|
else:
|
79
110
|
self.window.ui.nodes['output_plain'][pid].setVisible(False)
|
80
111
|
self.window.ui.nodes['output'][pid].setVisible(True)
|
81
|
-
self.switch_tab(Tab.TAB_CHAT)
|
112
|
+
#self.switch_tab(Tab.TAB_CHAT)
|
82
113
|
|
83
|
-
def on_tab_changed(self, idx: int):
|
114
|
+
def on_tab_changed(self, idx: int, column_idx: int = 0):
|
84
115
|
"""
|
85
116
|
Output tab changed
|
86
117
|
|
87
118
|
:param idx: tab index
|
119
|
+
:param column_idx: column index
|
88
120
|
"""
|
89
|
-
tab = self.window.core.tabs.get_tab_by_index(idx)
|
121
|
+
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
90
122
|
if tab is None:
|
91
123
|
self.appended = False
|
92
124
|
return
|
@@ -101,41 +133,53 @@ class Tabs:
|
|
101
133
|
|
102
134
|
|
103
135
|
prev_tab = self.current
|
136
|
+
prev_column = self.column_idx
|
104
137
|
self.current = idx
|
138
|
+
self.column_idx = column_idx
|
105
139
|
self.window.controller.ui.mode.update()
|
106
140
|
self.window.controller.ui.vision.update()
|
107
141
|
|
108
142
|
# check type
|
109
143
|
if tab.type == Tab.TAB_NOTEPAD:
|
110
144
|
self.window.controller.notepad.opened_once = True
|
111
|
-
self.window.controller.notepad.on_open(idx)
|
145
|
+
self.window.controller.notepad.on_open(idx, column_idx)
|
112
146
|
elif tab.type == Tab.TAB_CHAT:
|
113
|
-
|
114
|
-
|
147
|
+
# get meta for selected tab, if not loaded yet then append meta here
|
148
|
+
meta_id = self.window.core.ctx.output.prepare_meta(tab)
|
149
|
+
meta = self.window.core.ctx.get_meta_by_id(meta_id)
|
115
150
|
if meta is not None:
|
116
|
-
self.window.controller.ctx.load(
|
151
|
+
self.window.controller.ctx.load(meta.id) # reload renderer
|
117
152
|
elif tab.type == Tab.TAB_TOOL_PAINTER:
|
118
153
|
if self.window.core.config.get('vision.capture.enabled'):
|
119
154
|
self.window.controller.camera.enable_capture()
|
120
155
|
|
121
|
-
if prev_tab != idx:
|
156
|
+
if prev_tab != idx or prev_column != column_idx:
|
122
157
|
self.window.dispatch(AppEvent(AppEvent.TAB_SELECTED)) # app event
|
123
158
|
|
124
|
-
def get_current_idx(self) -> int:
|
159
|
+
def get_current_idx(self, column_idx: int = 0) -> int:
|
125
160
|
"""
|
126
161
|
Get current tab index
|
127
162
|
|
163
|
+
:param column_idx: column index
|
128
164
|
:return: tab index
|
129
165
|
"""
|
130
166
|
return self.current
|
131
167
|
|
168
|
+
def get_current_column_idx(self) -> int:
|
169
|
+
"""
|
170
|
+
Get current column index
|
171
|
+
|
172
|
+
:return: column index
|
173
|
+
"""
|
174
|
+
return self.column_idx
|
175
|
+
|
132
176
|
def get_current_tab(self) -> Tab or None:
|
133
177
|
"""
|
134
178
|
Get current tab
|
135
179
|
|
136
180
|
:return: tab
|
137
181
|
"""
|
138
|
-
return self.window.core.tabs.get_tab_by_index(self.
|
182
|
+
return self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
|
139
183
|
|
140
184
|
def get_current_type(self) -> int or None:
|
141
185
|
"""
|
@@ -143,7 +187,7 @@ class Tabs:
|
|
143
187
|
|
144
188
|
:return: tab type
|
145
189
|
"""
|
146
|
-
tab = self.window.core.tabs.get_tab_by_index(self.
|
190
|
+
tab = self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
|
147
191
|
if tab is None:
|
148
192
|
return None
|
149
193
|
return tab.type
|
@@ -154,7 +198,7 @@ class Tabs:
|
|
154
198
|
|
155
199
|
:return: tab PID
|
156
200
|
"""
|
157
|
-
tab = self.window.core.tabs.get_tab_by_index(self.
|
201
|
+
tab = self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
|
158
202
|
if tab is None:
|
159
203
|
return None
|
160
204
|
return tab.pid
|
@@ -166,7 +210,7 @@ class Tabs:
|
|
166
210
|
:param idx: tab index
|
167
211
|
:return: tab type
|
168
212
|
"""
|
169
|
-
tab = self.window.core.tabs.get_tab_by_index(idx)
|
213
|
+
tab = self.window.core.tabs.get_tab_by_index(idx, self.column_idx)
|
170
214
|
if tab is None:
|
171
215
|
return None
|
172
216
|
return tab.type
|
@@ -178,68 +222,105 @@ class Tabs:
|
|
178
222
|
:param type: tab type
|
179
223
|
:return: tab index
|
180
224
|
"""
|
181
|
-
return self.window.core.tabs.get_min_idx_by_type(type)
|
225
|
+
return self.window.core.tabs.get_min_idx_by_type(type, self.column_idx)
|
182
226
|
|
183
|
-
def
|
227
|
+
def on_column_changed(self):
|
228
|
+
"""Column changed event"""
|
229
|
+
if self.locked:
|
230
|
+
return
|
231
|
+
tab = self.window.core.tabs.get_tab_by_index(self.current, self.column_idx)
|
232
|
+
if tab is None:
|
233
|
+
return
|
234
|
+
current_ctx = self.window.core.ctx.get_current()
|
235
|
+
if current_ctx is not None and current_ctx != tab.data_id:
|
236
|
+
self.window.controller.ctx.select_on_list_only(tab.data_id)
|
237
|
+
|
238
|
+
def on_tab_clicked(self, idx: int, column_idx: int = 0):
|
184
239
|
"""
|
185
240
|
Tab click event
|
186
241
|
|
187
242
|
:param idx: tab index
|
243
|
+
:param column_idx: column index
|
244
|
+
"""
|
245
|
+
self.current = idx
|
246
|
+
self.column_idx = column_idx
|
247
|
+
self.on_column_changed()
|
248
|
+
|
249
|
+
def on_column_focus(self, idx: int):
|
250
|
+
"""
|
251
|
+
Column focus event
|
252
|
+
|
253
|
+
:param idx: column index
|
188
254
|
"""
|
189
|
-
|
255
|
+
self.column_idx = idx
|
256
|
+
self.on_column_changed()
|
190
257
|
|
191
|
-
def on_tab_dbl_clicked(self, idx: int):
|
258
|
+
def on_tab_dbl_clicked(self, idx: int, column_idx: int = 0):
|
192
259
|
"""
|
193
260
|
Tab double click event
|
194
261
|
|
195
262
|
:param idx: tab index
|
263
|
+
:param column_idx: column index
|
196
264
|
"""
|
197
|
-
|
265
|
+
self.column_idx = column_idx
|
266
|
+
self.on_tab_changed(idx, column_idx)
|
198
267
|
|
199
|
-
def on_tab_closed(self, idx: int):
|
268
|
+
def on_tab_closed(self, idx: int, column_idx: int = 0):
|
200
269
|
"""
|
201
270
|
Tab close event
|
202
271
|
|
203
272
|
:param idx: tab index
|
273
|
+
:param column_idx: column index
|
204
274
|
"""
|
205
|
-
self.window.core.tabs.remove_tab_by_idx(idx)
|
275
|
+
self.window.core.tabs.remove_tab_by_idx(idx, column_idx)
|
206
276
|
|
207
|
-
def on_tab_moved(self, idx: int):
|
277
|
+
def on_tab_moved(self, idx: int, column_idx: int = 0):
|
208
278
|
"""
|
209
279
|
Tab moved event
|
210
280
|
|
211
281
|
:param idx: tab index
|
282
|
+
:param column_idx: column index
|
212
283
|
"""
|
213
284
|
self.window.core.tabs.update()
|
214
285
|
|
215
|
-
def close(self, idx: int):
|
286
|
+
def close(self, idx: int, column_idx: int = 0):
|
216
287
|
"""
|
217
288
|
Close tab
|
218
289
|
|
219
290
|
:param idx: tab index
|
291
|
+
:param column_idx: column index
|
220
292
|
"""
|
221
|
-
self.on_tab_closed(idx)
|
293
|
+
self.on_tab_closed(idx, column_idx)
|
222
294
|
|
223
|
-
def close_all(
|
295
|
+
def close_all(
|
296
|
+
self,
|
297
|
+
type: int,
|
298
|
+
column_idx: int = 0,
|
299
|
+
force: bool = False
|
300
|
+
):
|
224
301
|
"""
|
225
302
|
Close all tabs
|
226
303
|
|
227
304
|
:param type: tab type
|
305
|
+
:param column_idx: column index
|
228
306
|
:param force: force close
|
229
307
|
"""
|
230
308
|
if not force:
|
309
|
+
self.tmp_column_idx = column_idx
|
231
310
|
self.window.ui.dialogs.confirm(
|
232
311
|
type='tab.close_all',
|
233
312
|
id=type,
|
234
313
|
msg=trans('tab.close_all.confirm'),
|
235
314
|
)
|
236
315
|
return
|
237
|
-
self.
|
316
|
+
column_idx = self.tmp_column_idx
|
317
|
+
self.window.core.tabs.remove_all_by_type(type, column_idx)
|
238
318
|
|
239
319
|
def next_tab(self):
|
240
320
|
"""Switch to next tab"""
|
241
|
-
|
242
|
-
|
321
|
+
tabs = self.window.ui.layout.get_active_tabs()
|
322
|
+
current = tabs.currentIndex()
|
323
|
+
all = len(tabs.children())
|
243
324
|
next = current + 1
|
244
325
|
if next >= all:
|
245
326
|
next = 0
|
@@ -247,8 +328,9 @@ class Tabs:
|
|
247
328
|
|
248
329
|
def prev_tab(self):
|
249
330
|
"""Switch to previous tab"""
|
250
|
-
|
251
|
-
|
331
|
+
tabs = self.window.ui.layout.get_active_tabs()
|
332
|
+
current = tabs.currentIndex()
|
333
|
+
all = len(tabs.children())
|
252
334
|
prev = current - 1
|
253
335
|
if prev < 0:
|
254
336
|
prev = all - 1
|
@@ -264,14 +346,16 @@ class Tabs:
|
|
264
346
|
if idx is not None:
|
265
347
|
self.switch_tab_by_idx(idx)
|
266
348
|
|
267
|
-
def switch_tab_by_idx(self, idx: int):
|
349
|
+
def switch_tab_by_idx(self, idx: int, column_idx: int = 0):
|
268
350
|
"""
|
269
351
|
Switch tab by index
|
270
352
|
|
271
353
|
:param idx: tab index
|
354
|
+
:param column_idx: column index
|
272
355
|
"""
|
273
|
-
self.window.ui.
|
274
|
-
|
356
|
+
tabs = self.window.ui.layout.get_tabs_by_idx(column_idx)
|
357
|
+
tabs.setCurrentIndex(idx)
|
358
|
+
self.on_tab_changed(idx, column_idx)
|
275
359
|
|
276
360
|
def get_current_tab_name(self) -> str:
|
277
361
|
"""
|
@@ -279,7 +363,8 @@ class Tabs:
|
|
279
363
|
|
280
364
|
:return: tab name
|
281
365
|
"""
|
282
|
-
|
366
|
+
tabs = self.window.ui.layout.get_active_tabs()
|
367
|
+
return tabs.tabText(self.current)
|
283
368
|
|
284
369
|
def get_current_tab_name_for_audio(self) -> str:
|
285
370
|
"""
|
@@ -309,18 +394,20 @@ class Tabs:
|
|
309
394
|
"""
|
310
395
|
Update tab tooltip
|
311
396
|
|
312
|
-
:param tooltip: tooltip
|
397
|
+
:param tooltip: tooltip text
|
313
398
|
"""
|
314
|
-
self.window.ui.
|
399
|
+
tabs = self.window.ui.layout.get_active_tabs()
|
400
|
+
tabs.setTabToolTip(self.current, tooltip)
|
315
401
|
|
316
|
-
def rename(self, idx: int):
|
402
|
+
def rename(self, idx: int, column_idx: int = 0):
|
317
403
|
"""
|
318
|
-
Rename tab
|
404
|
+
Rename tab (show dialog)
|
319
405
|
|
320
406
|
:param idx: tab idx
|
407
|
+
:param column_idx: column idx
|
321
408
|
"""
|
322
409
|
# get tab
|
323
|
-
tab = self.window.core.tabs.get_tab_by_index(idx)
|
410
|
+
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
324
411
|
if tab is None:
|
325
412
|
return
|
326
413
|
# set dialog and show
|
@@ -329,7 +416,12 @@ class Tabs:
|
|
329
416
|
self.window.ui.dialog['rename'].current = idx
|
330
417
|
self.window.ui.dialog['rename'].show()
|
331
418
|
|
332
|
-
def update_name(
|
419
|
+
def update_name(
|
420
|
+
self,
|
421
|
+
idx: int,
|
422
|
+
name: str,
|
423
|
+
close: bool = True
|
424
|
+
):
|
333
425
|
"""
|
334
426
|
Update tab title
|
335
427
|
|
@@ -349,7 +441,11 @@ class Tabs:
|
|
349
441
|
"""
|
350
442
|
self.update_name(self.current, name)
|
351
443
|
|
352
|
-
def update_title(
|
444
|
+
def update_title(
|
445
|
+
self,
|
446
|
+
idx: int,
|
447
|
+
title: str
|
448
|
+
):
|
353
449
|
"""
|
354
450
|
Update tab title
|
355
451
|
|
@@ -359,8 +455,9 @@ class Tabs:
|
|
359
455
|
# check if current tab is chat
|
360
456
|
if self.get_current_type() != Tab.TAB_CHAT:
|
361
457
|
return
|
458
|
+
tabs = self.window.ui.layout.get_active_tabs()
|
362
459
|
tooltip = title
|
363
|
-
|
460
|
+
tabs.setTabToolTip(idx, tooltip)
|
364
461
|
if len(title) > 8:
|
365
462
|
title = title[:8] + '...' # truncate to max 8 chars
|
366
463
|
self.window.core.tabs.update_title(idx, title, tooltip)
|
@@ -383,7 +480,53 @@ class Tabs:
|
|
383
480
|
if idx is not None:
|
384
481
|
self.switch_tab_by_idx(idx)
|
385
482
|
|
386
|
-
def new_tab(self):
|
387
|
-
"""
|
388
|
-
|
389
|
-
|
483
|
+
def new_tab(self, column_idx: int = 0):
|
484
|
+
"""
|
485
|
+
Handle [+] button
|
486
|
+
|
487
|
+
:param column_idx: column index
|
488
|
+
"""
|
489
|
+
idx = self.get_current_idx(column_idx)
|
490
|
+
self.append(Tab.TAB_CHAT, idx, column_idx)
|
491
|
+
|
492
|
+
def restore_data(self):
|
493
|
+
"""Restore tab data"""
|
494
|
+
data = self.window.core.config.get("tabs.opened", [])
|
495
|
+
if not data:
|
496
|
+
self.switch_tab_by_idx(0, 0)
|
497
|
+
return
|
498
|
+
for col_idx in data:
|
499
|
+
tab_idx = data[col_idx]
|
500
|
+
self.switch_tab_by_idx(int(tab_idx), int(col_idx))
|
501
|
+
|
502
|
+
# set default column to 0
|
503
|
+
self.column_idx = 0
|
504
|
+
self.on_column_changed()
|
505
|
+
|
506
|
+
def move_tab(self, idx: int, column_idx: int, new_column_idx: int):
|
507
|
+
"""
|
508
|
+
Move tab to another column
|
509
|
+
|
510
|
+
:param idx: tab index
|
511
|
+
:param column_idx: column index
|
512
|
+
:param new_column_idx: new column index
|
513
|
+
"""
|
514
|
+
self.locked = True
|
515
|
+
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
516
|
+
self.window.core.tabs.move_tab(tab, new_column_idx)
|
517
|
+
self.locked = False
|
518
|
+
|
519
|
+
def toggle_split_screen(self, state):
|
520
|
+
"""
|
521
|
+
Toggle split screen
|
522
|
+
|
523
|
+
:param state: state
|
524
|
+
"""
|
525
|
+
if state:
|
526
|
+
#self.rightWidget.show()
|
527
|
+
self.window.ui.splitters['columns'].setSizes([1, 1])
|
528
|
+
else:
|
529
|
+
#self.rightWidget.hide()
|
530
|
+
self.window.ui.splitters['columns'].setSizes([1, 0])
|
531
|
+
self.window.core.config.set("layout.split", state)
|
532
|
+
self.window.core.config.save()
|