add Mom/Tog to ToggleWidget, Big Title, Layout panel scaffold
- ToggleWidget: add Momentary/Toggle mode selector (mirrors TransportWidget minus OSC); HW routing respects trigger_mode; fix invisible button on init by removing explicit empty stylesheet; saves/loads trigger_mode per preset - Big Title: full-width 28px bold QLineEdit above fader row, saves per-preset as big_title; green underline on focus; single bottom border, no separator - Layout panel: floating 220px window to the left of main window, toggled by new Layout button next to Messages; shows Sections checkboxes (Preset Browser, Big Title, Fader Row, Toggle Row, Transport Row); tracks window move/resize; checkboxes not yet wired to functionality Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,7 @@ view_uuid_error_overlay = None
|
||||
label_overlay_message = None
|
||||
placeholder_fader_empty = None
|
||||
placeholder_toggle_empty = None
|
||||
big_title_edit = None
|
||||
|
||||
|
||||
|
||||
@@ -346,6 +347,11 @@ button_osc_messages_log = QPushButton("Messages")
|
||||
button_osc_messages_log.setFixedWidth(80)
|
||||
button_osc_messages_log.setCheckable(True)
|
||||
|
||||
# Widget: button_layout_panel
|
||||
button_layout_panel = QPushButton("Layout")
|
||||
button_layout_panel.setFixedWidth(65)
|
||||
button_layout_panel.setCheckable(True)
|
||||
|
||||
|
||||
# Layout: hstack_osc_status
|
||||
hstack_osc_status = QHBoxLayout()
|
||||
@@ -553,6 +559,54 @@ textedit_midi_out_log.setStyleSheet("""
|
||||
}
|
||||
""")
|
||||
|
||||
# MARK: LAYOUT PANEL
|
||||
container_layout_panel = QWidget(None, Qt.WindowType.Window)
|
||||
container_layout_panel.setWindowTitle("Layout")
|
||||
container_layout_panel.setStyleSheet("background-color: #1a1a1a;")
|
||||
container_layout_panel.setFixedWidth(220)
|
||||
|
||||
vstack_layout_panel = QVBoxLayout(container_layout_panel)
|
||||
vstack_layout_panel.setContentsMargins(10, 10, 10, 10)
|
||||
vstack_layout_panel.setSpacing(6)
|
||||
|
||||
label_layout_panel_title = QLabel("Sections")
|
||||
label_layout_panel_title.setStyleSheet("color: #ffffff; font-weight: bold; font-size: 12px;")
|
||||
vstack_layout_panel.addWidget(label_layout_panel_title)
|
||||
|
||||
_section_sep = QFrame()
|
||||
_section_sep.setFrameShape(QFrame.Shape.HLine)
|
||||
_section_sep.setStyleSheet("color: #3a3a3a;")
|
||||
vstack_layout_panel.addWidget(_section_sep)
|
||||
|
||||
# Section rows (layout only — not wired yet)
|
||||
_section_rows = [
|
||||
("Preset Browser", "chk_section_preset_browser"),
|
||||
("Big Title", "chk_section_big_title"),
|
||||
("Fader Row", "chk_section_fader_row"),
|
||||
("Toggle Row", "chk_section_toggle_row"),
|
||||
("Transport Row", "chk_section_transport_row"),
|
||||
]
|
||||
from PyQt6.QtWidgets import QCheckBox
|
||||
for _lbl, _attr in _section_rows:
|
||||
_row = QWidget()
|
||||
_row.setStyleSheet("background: transparent;")
|
||||
_hbox = QHBoxLayout(_row)
|
||||
_hbox.setContentsMargins(4, 2, 4, 2)
|
||||
_hbox.setSpacing(8)
|
||||
_chk = QCheckBox()
|
||||
_chk.setChecked(True)
|
||||
_chk.setFixedSize(16, 16)
|
||||
_name_lbl = QLabel(_lbl)
|
||||
_name_lbl.setStyleSheet("color: #d4d4d4; font-size: 12px; background: transparent;")
|
||||
_hbox.addWidget(_chk)
|
||||
_hbox.addWidget(_name_lbl)
|
||||
_hbox.addStretch()
|
||||
globals()[_attr] = _chk
|
||||
vstack_layout_panel.addWidget(_row)
|
||||
del _lbl, _attr, _row, _hbox, _chk, _name_lbl, _section_rows
|
||||
|
||||
vstack_layout_panel.addStretch()
|
||||
|
||||
# MARK: PRESET BROWSER ROW MODULE
|
||||
label_preset_browser_title = QLabel("Preset Browser")
|
||||
hstack_preset_browser = QHBoxLayout()
|
||||
@@ -931,6 +985,7 @@ class MainWindow(QMainWindow):
|
||||
# build hstack for control buttons
|
||||
hstack_osc_buttons.addWidget(button_osc_toggle)
|
||||
hstack_osc_buttons.addWidget(button_osc_messages_log)
|
||||
hstack_osc_buttons.addWidget(button_layout_panel)
|
||||
hstack_osc_buttons.addStretch()
|
||||
|
||||
# add all rows to vstack
|
||||
@@ -944,6 +999,7 @@ class MainWindow(QMainWindow):
|
||||
spinner_osc_send_port.valueChanged.connect(update_osc_server_port)
|
||||
button_osc_toggle.clicked.connect(start_stop_osc_server)
|
||||
button_osc_messages_log.clicked.connect(log_ui_show_hide)
|
||||
button_layout_panel.clicked.connect(layout_panel_show_hide)
|
||||
|
||||
# add the combined stack to the row
|
||||
hstack_io_row.addLayout(vstack_osc)
|
||||
@@ -1061,6 +1117,9 @@ class MainWindow(QMainWindow):
|
||||
button_midi_out_clear.clicked.connect(lambda: textedit_midi_out_log.clear())
|
||||
container_midi_out_log.setVisible(False)
|
||||
|
||||
#MARK: LAYOUT PANEL
|
||||
container_layout_panel.setVisible(False)
|
||||
|
||||
main_layout.addWidget(make_separator())
|
||||
|
||||
# MARK: TRANSPORT ROW CONTROLS
|
||||
@@ -1122,12 +1181,22 @@ class MainWindow(QMainWindow):
|
||||
global container_rows_and_widgets
|
||||
global view_uuid_error_overlay
|
||||
global label_overlay_message
|
||||
global big_title_edit
|
||||
container_rows_and_widgets = QWidget()
|
||||
controls_layout = QVBoxLayout(container_rows_and_widgets)
|
||||
controls_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(container_rows_and_widgets)
|
||||
|
||||
controls_layout.addWidget(make_separator())
|
||||
# MARK: BIG TITLE
|
||||
big_title_edit = QLineEdit("")
|
||||
big_title_edit.setPlaceholderText("Title")
|
||||
big_title_edit.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
big_title_edit.setStyleSheet(
|
||||
"QLineEdit { background: transparent; border: none; border-bottom: 1px solid #3a3a3a;"
|
||||
" color: #ffffff; font-size: 28px; font-weight: bold; padding: 4px 0; }"
|
||||
"QLineEdit:focus { border-bottom: 1px solid #00e676; }"
|
||||
)
|
||||
controls_layout.addWidget(big_title_edit)
|
||||
|
||||
# MARK: FADER ROW CONTROLS
|
||||
global hstack_row_fader_control_buttons
|
||||
@@ -1288,11 +1357,15 @@ class MainWindow(QMainWindow):
|
||||
super().resizeEvent(event)
|
||||
if container_osc_in_log.isVisible():
|
||||
log_ui_window_calc()
|
||||
if container_layout_panel.isVisible():
|
||||
layout_panel_window_calc()
|
||||
|
||||
def moveEvent(self, event):
|
||||
super().moveEvent(event)
|
||||
if container_osc_in_log.isVisible():
|
||||
log_ui_window_calc()
|
||||
if container_layout_panel.isVisible():
|
||||
layout_panel_window_calc()
|
||||
|
||||
def make_separator():
|
||||
sep = QWidget()
|
||||
@@ -1347,12 +1420,13 @@ def new_blank_preset():
|
||||
if not ok or not name.strip():
|
||||
return
|
||||
name = name.strip()
|
||||
loaded_presets["presets"][name] = {"faders": [], "toggles": [], "preset_uuid": "", "browser": {"back": preset_browser_back.get_state(), "fwd": preset_browser_fwd.get_state()}, "show_hide": {}}
|
||||
loaded_presets["presets"][name] = {"faders": [], "toggles": [], "preset_uuid": "", "browser": {"back": preset_browser_back.get_state(), "fwd": preset_browser_fwd.get_state()}, "show_hide": {}, "big_title": ""}
|
||||
loaded_presets["__last_used__"] = name
|
||||
save_presets_file(loaded_presets)
|
||||
populate_preset_dropdown_menu()
|
||||
menuPresets.setCurrentText(name)
|
||||
clear_all_strips()
|
||||
big_title_edit.setText("")
|
||||
show_save_status()
|
||||
|
||||
def show_save_status():
|
||||
@@ -1904,6 +1978,7 @@ def preset_build_snapshot(name):
|
||||
"back": preset_browser_back.get_state(),
|
||||
"fwd": preset_browser_fwd.get_state(),
|
||||
},
|
||||
"big_title": big_title_edit.text(),
|
||||
"preset_uuid": label_preset_uuid.text().strip(),
|
||||
"show_hide": {
|
||||
"fader": fader_show_hide_config,
|
||||
@@ -2024,6 +2099,7 @@ def preset_load(preset):
|
||||
button_preset_browser_show_hide_config.setText("Show Parameters" if show_hide.get("browser", False) else "Hide Parameters")
|
||||
button_preset_browser_show_hide_config.setChecked(show_hide.get("browser", False))
|
||||
|
||||
big_title_edit.setText(preset.get("big_title", ""))
|
||||
label_preset_uuid.setText(preset.get("preset_uuid", ""))
|
||||
check_and_index_strip_labels()
|
||||
|
||||
@@ -2249,13 +2325,19 @@ def midi_message_route_hw_to_output_cc(fader_uid, value):
|
||||
if toggle.uid == fader_uid:
|
||||
out_cc = toggle.cc_spin.value()
|
||||
out_ch = toggle.ch_spin.value()
|
||||
print(f"[toggle convert] HW CC{toggle.hw_cc} CH{toggle.hw_channel} ({value}) → CC{out_cc} CH{out_ch} ({value})")
|
||||
print(f"[toggle convert] HW CC{toggle.hw_cc} CH{toggle.hw_channel} ({value}) → CC{out_cc} CH{out_ch}")
|
||||
toggle.cc_input_lbl.setText(toggle._cc_input_label(value))
|
||||
if toggle.trigger_mode == "momentary":
|
||||
send_cc(out_cc, 127, channel=out_ch)
|
||||
toggle.cc_output_lbl.setText(toggle._cc_output_label(127))
|
||||
toggle.led_dot.setStyleSheet("background-color: #00e676; border-radius: 3px;")
|
||||
QTimer.singleShot(150, lambda: toggle.led_dot.setStyleSheet("background-color: #3c3c3c; border-radius: 3px;"))
|
||||
else:
|
||||
should_be_on = value > 63
|
||||
if toggle.toggle_state != should_be_on:
|
||||
toggle.toggle_state = should_be_on
|
||||
out_val = 127 if should_be_on else 0
|
||||
send_cc(out_cc, out_val, channel=out_ch)
|
||||
toggle.cc_input_lbl.setText(toggle._cc_input_label(value))
|
||||
toggle.cc_output_lbl.setText(toggle._cc_output_label(out_val))
|
||||
toggle._update_btn_style()
|
||||
return
|
||||
@@ -2312,6 +2394,20 @@ def log_ui_window_calc():
|
||||
container_midi_out_log.setFixedHeight(half)
|
||||
container_midi_out_log.move(geo.right() + 368, geo.top() + half + 4)
|
||||
|
||||
|
||||
def layout_panel_show_hide(checked):
|
||||
container_layout_panel.setVisible(checked)
|
||||
if checked:
|
||||
layout_panel_window_calc()
|
||||
|
||||
|
||||
def layout_panel_window_calc():
|
||||
geo = app_window.frameGeometry()
|
||||
container_layout_panel.setFixedWidth(220)
|
||||
container_layout_panel.setFixedHeight(geo.height())
|
||||
container_layout_panel.move(geo.left() - 224, geo.top())
|
||||
|
||||
|
||||
def start_osc_server(port=9000):
|
||||
global _osc_server, _osc_thread
|
||||
stop_osc_server()
|
||||
|
||||
+22
-5
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"__last_used__": "preset3",
|
||||
"__last_used__": "preset5",
|
||||
"presets": {
|
||||
"preset1": {
|
||||
"faders": [
|
||||
@@ -244,7 +244,22 @@
|
||||
"zone_index": -1
|
||||
}
|
||||
],
|
||||
"toggles": [],
|
||||
"toggles": [
|
||||
{
|
||||
"uid": "e291394c-cc09-4d3c-9ac3-ed5fe2803a46",
|
||||
"label": "Toggle 1",
|
||||
"cc": 20,
|
||||
"ch": 1,
|
||||
"state": false,
|
||||
"color": "Gray",
|
||||
"zone": null,
|
||||
"center_index": null,
|
||||
"zone_index": null,
|
||||
"hw_cc": null,
|
||||
"hw_channel": null,
|
||||
"trigger_mode": "toggle"
|
||||
}
|
||||
],
|
||||
"browser": {
|
||||
"back": {
|
||||
"label": "Back",
|
||||
@@ -398,7 +413,8 @@
|
||||
"center_index": null,
|
||||
"zone_index": null,
|
||||
"hw_cc": null,
|
||||
"hw_channel": null
|
||||
"hw_channel": null,
|
||||
"trigger_mode": "toggle"
|
||||
}
|
||||
],
|
||||
"browser": {
|
||||
@@ -419,6 +435,7 @@
|
||||
"trigger_mode": "momentary"
|
||||
}
|
||||
},
|
||||
"big_title": "STRING SHORTS",
|
||||
"preset_uuid": "3e9d51d2-cf55-49f4-80a4-2f7f4466f959",
|
||||
"show_hide": {
|
||||
"fader": false,
|
||||
@@ -510,10 +527,10 @@
|
||||
"trigger_mode": "momentary"
|
||||
}
|
||||
],
|
||||
"transport_show_hide": false,
|
||||
"transport_show_hide": true,
|
||||
"io_config": {
|
||||
"midi_out": "IAC Driver Bus 5",
|
||||
"midi_in": "VMPK Output",
|
||||
"midi_in": "Not Connected",
|
||||
"transport_out": "Not Connected",
|
||||
"osc_ip": "127.0.0.1",
|
||||
"osc_listen_port": 9000,
|
||||
|
||||
+45
-3
@@ -4,7 +4,7 @@ from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
from palette import PALETTE_HEX, PALETTE_VIVID
|
||||
from styles import TITLE_STYLE
|
||||
@@ -19,6 +19,7 @@ class ToggleWidget(QWidget):
|
||||
self.setFixedWidth(115)
|
||||
self.current_color = PALETTE_HEX.get("Gray", "#263238")
|
||||
self.toggle_state = False
|
||||
self.trigger_mode = "toggle"
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.zone = None
|
||||
self.center_index = None
|
||||
@@ -108,6 +109,26 @@ class ToggleWidget(QWidget):
|
||||
_hbox_ch.addWidget(self.ch_spin)
|
||||
_grp2_layout.addWidget(self.ch_spin_row)
|
||||
|
||||
# Momentary / Toggle mode selector
|
||||
trigger_row = QHBoxLayout()
|
||||
trigger_row.setContentsMargins(0, 0, 0, 0)
|
||||
trigger_row.setSpacing(2)
|
||||
self.momentary_btn = QPushButton("Mom.")
|
||||
self.momentary_btn.setFixedHeight(18)
|
||||
self.momentary_btn.setCheckable(True)
|
||||
self.momentary_btn.setChecked(False)
|
||||
self.momentary_btn.setStyleSheet("border: none;")
|
||||
self.toggle_mode_btn = QPushButton("Tog.")
|
||||
self.toggle_mode_btn.setFixedHeight(18)
|
||||
self.toggle_mode_btn.setCheckable(True)
|
||||
self.toggle_mode_btn.setChecked(True)
|
||||
self.toggle_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.momentary_btn.clicked.connect(lambda: self.set_trigger_mode("momentary"))
|
||||
self.toggle_mode_btn.clicked.connect(lambda: self.set_trigger_mode("toggle"))
|
||||
trigger_row.addWidget(self.momentary_btn)
|
||||
trigger_row.addWidget(self.toggle_mode_btn)
|
||||
_grp2_layout.addLayout(trigger_row)
|
||||
|
||||
self.cc_output_lbl = QLabel(f"CC{default_cc} [--]")
|
||||
self.cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
@@ -122,7 +143,6 @@ class ToggleWidget(QWidget):
|
||||
|
||||
self.btn = QPushButton("", btn_container)
|
||||
self.btn.setFixedSize(60, 60)
|
||||
self.btn.setStyleSheet("")
|
||||
self.btn.clicked.connect(self.on_click)
|
||||
|
||||
self.led_dot = QLabel(btn_container)
|
||||
@@ -289,10 +309,30 @@ class ToggleWidget(QWidget):
|
||||
self.apply_color(hex_color)
|
||||
self._update_btn_style()
|
||||
|
||||
def set_trigger_mode(self, mode):
|
||||
self.trigger_mode = mode
|
||||
if mode == "momentary":
|
||||
self.momentary_btn.setChecked(True)
|
||||
self.toggle_mode_btn.setChecked(False)
|
||||
self.momentary_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.toggle_mode_btn.setStyleSheet("")
|
||||
else:
|
||||
self.toggle_mode_btn.setChecked(True)
|
||||
self.momentary_btn.setChecked(False)
|
||||
self.toggle_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.momentary_btn.setStyleSheet("")
|
||||
|
||||
def on_click(self):
|
||||
self.toggle_state = not self.toggle_state
|
||||
cc_num = self.cc_spin.value()
|
||||
out_ch = self.ch_spin.value()
|
||||
if self.trigger_mode == "momentary":
|
||||
print(f"[toggle momentary] CC{cc_num} CH{out_ch} (127)")
|
||||
send_cc(cc_num, 127, channel=out_ch)
|
||||
self.cc_output_lbl.setText(self._cc_output_label(127))
|
||||
self.led_dot.setStyleSheet("background-color: #00e676; border-radius: 3px;")
|
||||
QTimer.singleShot(150, lambda: self.led_dot.setStyleSheet("background-color: #3c3c3c; border-radius: 3px;"))
|
||||
else:
|
||||
self.toggle_state = not self.toggle_state
|
||||
value = 127 if self.toggle_state else 0
|
||||
print(f"[toggle] CC{cc_num} CH{out_ch} ({value})")
|
||||
send_cc(cc_num, value, channel=out_ch)
|
||||
@@ -318,6 +358,7 @@ class ToggleWidget(QWidget):
|
||||
"zone_index": self.zone_index,
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"trigger_mode": self.trigger_mode,
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
@@ -341,6 +382,7 @@ class ToggleWidget(QWidget):
|
||||
self.zone_btn.set_state(self.zone == "left", self.zone == "right")
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
self.set_trigger_mode(state.get("trigger_mode", "toggle"))
|
||||
if self.hw_cc is not None:
|
||||
self.learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.cc_input_lbl.setText(self._cc_input_label())
|
||||
|
||||
Reference in New Issue
Block a user