session rename: double-click to rename sessions in tree
This commit is contained in:
parent
2267c57b54
commit
63cf13b891
130
gui/main.qml
130
gui/main.qml
|
|
@ -146,6 +146,36 @@ ApplicationWindow {
|
||||||
ollie.switchAgent(parentId, agentId)
|
ollie.switchAgent(parentId, agentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rename tracking
|
||||||
|
property int renamingIndex: -1
|
||||||
|
property string renamingText: ""
|
||||||
|
|
||||||
|
function startRename(index) {
|
||||||
|
var item = sessionTreeModel.get(index)
|
||||||
|
if (item.type === "session") {
|
||||||
|
renamingIndex = index
|
||||||
|
renamingText = item.displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitRename() {
|
||||||
|
if (renamingIndex < 0) return
|
||||||
|
var item = sessionTreeModel.get(renamingIndex)
|
||||||
|
var newName = renamingText.trim()
|
||||||
|
if (newName !== "" && newName !== item.id) {
|
||||||
|
if (ollie.renameSession(item.id, newName)) {
|
||||||
|
sessionTreeModel.set(renamingIndex, { id: newName, displayName: newName })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renamingIndex = -1
|
||||||
|
renamingText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelRename() {
|
||||||
|
renamingIndex = -1
|
||||||
|
renamingText = ""
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ollie
|
target: ollie
|
||||||
function onSessionsChanged() {
|
function onSessionsChanged() {
|
||||||
|
|
@ -500,27 +530,95 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
visible: height > 0
|
visible: height > 0
|
||||||
|
|
||||||
|
// Click disambiguation: timer-based single vs double click
|
||||||
|
property int _clickCount: 0
|
||||||
|
Timer {
|
||||||
|
id: clickTimer
|
||||||
|
interval: 300
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
parent._clickCount = 0
|
||||||
|
if (type === "session" && index !== renamingIndex) {
|
||||||
|
toggleExpand(index)
|
||||||
|
} else if (type === "agent") {
|
||||||
|
activateAgent(parentId, agentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
hoverEnabled: false
|
||||||
|
onClicked: function() {
|
||||||
|
if (renamingIndex >= 0) return
|
||||||
|
parent._clickCount++
|
||||||
|
if (parent._clickCount === 1) {
|
||||||
|
clickTimer.start()
|
||||||
|
} else if (parent._clickCount === 2) {
|
||||||
|
clickTimer.stop()
|
||||||
|
parent._clickCount = 0
|
||||||
|
if (type === "session") {
|
||||||
|
startRename(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// Indent + expand/collapse indicator
|
// Indent + expand/collapse indicator
|
||||||
Label {
|
Item {
|
||||||
Layout.preferredWidth: (level || 0) * 16 + 16
|
Layout.preferredWidth: (level || 0) * 16 + 16
|
||||||
text: type === "session" ? (expanded ? "▼" : "▶") : ""
|
Layout.preferredHeight: 28
|
||||||
font.pixelSize: theme.fontSize > 0 ? theme.fontSize * 0.7 : 10
|
|
||||||
color: theme.textColor
|
Label {
|
||||||
opacity: 0.6
|
anchors.centerIn: parent
|
||||||
verticalAlignment: Text.AlignVCenter
|
text: type === "session" ? (expanded ? "▼" : "▶") : ""
|
||||||
|
font.pixelSize: theme.fontSize > 0 ? theme.fontSize * 0.7 : 10
|
||||||
|
color: theme.textColor
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name
|
// Name area
|
||||||
Label {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: displayName || ""
|
Layout.preferredHeight: 28
|
||||||
color: theme.textColor
|
|
||||||
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
|
TextField {
|
||||||
verticalAlignment: Text.AlignVCenter
|
anchors.fill: parent
|
||||||
|
text: (index === renamingIndex) ? renamingText : ""
|
||||||
|
visible: index === renamingIndex
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
|
||||||
|
color: theme.textColor
|
||||||
|
background: Rectangle {
|
||||||
|
color: theme.baseColor
|
||||||
|
border.color: theme.highlightColor
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
Keys.onReturnPressed: commitRename()
|
||||||
|
Keys.onEscapePressed: cancelRename()
|
||||||
|
onAccepted: commitRename()
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
forceActiveFocus()
|
||||||
|
selectAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.fill: parent
|
||||||
|
text: displayName || ""
|
||||||
|
color: theme.textColor
|
||||||
|
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
visible: index !== renamingIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// State dot
|
// State dot
|
||||||
|
|
@ -538,14 +636,6 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (type === "session") {
|
|
||||||
toggleExpand(index)
|
|
||||||
} else if (type === "agent") {
|
|
||||||
activateAgent(parentId, agentId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
highlighted: (type === "agent" && agentId === ollie.activeAgentId && parentId === ollie.activeSessionId)
|
highlighted: (type === "agent" && agentId === ollie.activeAgentId && parentId === ollie.activeSessionId)
|
||||||
|
|
||||||
function parentSessionExpanded(pid) {
|
function parentSessionExpanded(pid) {
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,42 @@ bool Ollie9pClient::createSession(const QString &cwd, const QString &name, const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Ollie9pClient::renameSession(const QString &sessionId, const QString &newName)
|
||||||
|
{
|
||||||
|
if (sessionId.isEmpty() || newName.isEmpty()) return false;
|
||||||
|
|
||||||
|
// Use Twstat to rename the session directory.
|
||||||
|
// We need to open the directory and write its stat.
|
||||||
|
// 9p doesn't have a direct "mv" command, but we can use stat/rdwr
|
||||||
|
// to read the current stat, modify the name, and write it back.
|
||||||
|
// However, plan9port's 9p doesn't support wstat directly.
|
||||||
|
//
|
||||||
|
// Alternative: use our own ollie-9p which has a mv command.
|
||||||
|
QString bin = QStandardPaths::findExecutable("ollie-9p");
|
||||||
|
if (bin.isEmpty()) {
|
||||||
|
qDebug() << "renameSession: ollie-9p not found, cannot rename";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(bin, {"-a", serverAddr(), "mv", "session/" + sessionId, "session/" + newName});
|
||||||
|
proc.waitForFinished(5000);
|
||||||
|
|
||||||
|
if (proc.exitCode() != 0) {
|
||||||
|
qDebug() << "renameSession failed:" << QString::fromUtf8(proc.readAllStandardError()).trimmed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update active session ID if we renamed the active session
|
||||||
|
if (m_activeSessionId == sessionId) {
|
||||||
|
m_activeSessionId = newName;
|
||||||
|
emit activeSessionIdChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSessions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Root data loading (lazy) ---
|
// --- Root data loading (lazy) ---
|
||||||
|
|
||||||
void Ollie9pClient::ensureRootDataLoaded()
|
void Ollie9pClient::ensureRootDataLoaded()
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ public:
|
||||||
Q_INVOKABLE void loadRootAgents();
|
Q_INVOKABLE void loadRootAgents();
|
||||||
Q_INVOKABLE QStringList getAvailableModels(const QString &backend) const;
|
Q_INVOKABLE QStringList getAvailableModels(const QString &backend) const;
|
||||||
Q_INVOKABLE bool createSession(const QString &cwd, const QString &name, const QString &backend, const QString &model, const QString &agent, const QString &remote);
|
Q_INVOKABLE bool createSession(const QString &cwd, const QString &name, const QString &backend, const QString &model, const QString &agent, const QString &remote);
|
||||||
|
Q_INVOKABLE bool renameSession(const QString &sessionId, const QString &newName);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sessionsChanged();
|
void sessionsChanged();
|
||||||
|
|
|
||||||
Reference in New Issue