11import * as vscode from 'vscode' ;
22import { isMat , isPoint3Vector , is1DVector , isLikely1DMat , is1DSet , isMatx , is2DStdArray , is1DStdArray , isPoint3StdArray , is2DCStyleArray , is1DCStyleArray , is3DCStyleArray , is3DStdArray , isUninitializedOrInvalid , isUninitializedMat , isUninitializedMatFromChildren , isUninitializedVector , isPointerType , getPointerEvaluateExpression } from './utils/opencv' ;
33import { SyncManager } from './utils/syncManager' ;
4+ import { PanelManager } from './utils/panelManager' ;
45
56const COLORS = [
67 '#3794ef' , // Blue
@@ -34,6 +35,14 @@ function getColoredIcon(kind: 'mat' | 'pointcloud' | 'plot' | 'group', color: st
3435 return { light : iconUri , dark : iconUri } ;
3536}
3637
38+ function getColoredCircleIcon ( color : string ) : { light : vscode . Uri , dark : vscode . Uri } {
39+ // Create a simple filled circle SVG
40+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><circle cx="8" cy="8" r="6" fill="${ color } "/></svg>` ;
41+ const base64 = Buffer . from ( svg ) . toString ( 'base64' ) ;
42+ const iconUri = vscode . Uri . parse ( `data:image/svg+xml;base64,${ base64 } ` ) ;
43+ return { light : iconUri , dark : iconUri } ;
44+ }
45+
3746export class CVVariable extends vscode . TreeItem {
3847 public readonly isEmpty : boolean ;
3948
@@ -51,7 +60,8 @@ export class CVVariable extends vscode.TreeItem {
5160 public pairedWith ?: string ,
5261 public groupIndex ?: number ,
5362 public readonly isPointer : boolean = false ,
54- public readonly baseType : string = ''
63+ public readonly baseType : string = '' ,
64+ public readonly sessionId ?: string // Add sessionId to check panel status
5565 ) {
5666 super ( name , collapsibleState ) ;
5767 this . tooltip = `${ this . name } : ${ this . type } ${ this . pairedWith ? ` (Paired with ${ this . pairedWith } )` : '' } ${ this . isPointer ? ' (pointer)' : '' } ` ;
@@ -63,17 +73,28 @@ export class CVVariable extends vscode.TreeItem {
6373 this . isEmpty = ( sizeInfo === '0' || sizeInfo === '0x0' || sizeInfo === '' || size === 0 ) ;
6474 const displaySize = this . isEmpty ? 'empty' : sizeInfo ;
6575
66- // Show pointer indicator in description
76+ // Check if panel is open
77+ const isOpen = sessionId ? this . checkIfPanelOpen ( kind , sessionId , name ) : false ;
78+
79+ // Show pointer indicator and open status in description
6780 const pointerIndicator = this . isPointer ? '→ ' : '' ;
68- this . description = `${ pointerIndicator } [${ displaySize } ]` ;
81+ const openIndicator = isOpen ? '● ' : '' ;
82+ this . description = `${ openIndicator } ${ pointerIndicator } [${ displaySize } ]` ;
6983
70- if ( isPaired && groupIndex !== undefined && kind !== 'plot' ) {
84+ // For mat (image) variables, always use file-media icon regardless of pairing
85+ if ( kind === 'mat' ) {
86+ this . iconPath = new vscode . ThemeIcon ( 'file-media' ) ;
87+ this . contextValue = isPaired
88+ ? `cvVariablePaired:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ${ isOpen ? ':open' : '' } `
89+ : `cvVariable:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ${ isOpen ? ':open' : '' } ` ;
90+ } else if ( isPaired && groupIndex !== undefined && kind !== 'plot' ) {
91+ // For pointcloud, use colored icon when paired
7192 const color = COLORS [ groupIndex % COLORS . length ] ;
7293 this . iconPath = getColoredIcon ( kind , color ) ;
73- this . contextValue = `cvVariablePaired:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ` ;
94+ this . contextValue = `cvVariablePaired:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ${ isOpen ? ':open' : '' } ` ;
7495 } else {
7596 this . iconPath = new vscode . ThemeIcon ( typeIcon ) ;
76- this . contextValue = `cvVariable:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ` ;
97+ this . contextValue = `cvVariable:${ kind } ${ this . isEmpty ? ':empty' : '' } ${ this . isPointer ? ':pointer' : '' } ${ isOpen ? ':open' : '' } ` ;
7798 }
7899
79100 if ( ! this . isEmpty ) {
@@ -84,6 +105,38 @@ export class CVVariable extends vscode.TreeItem {
84105 } ;
85106 }
86107 }
108+
109+ private checkIfPanelOpen ( kind : string , sessionId : string , variableName : string ) : boolean {
110+ const viewTypeMap : Record < string , string > = {
111+ 'mat' : 'MatImageViewer' ,
112+ 'pointcloud' : 'PointCloudViewer' ,
113+ 'plot' : 'PlotViewer'
114+ } ;
115+ const viewType = viewTypeMap [ kind ] ;
116+ if ( ! viewType ) return false ;
117+ return PanelManager . hasPanel ( viewType , sessionId , variableName ) ;
118+ }
119+ }
120+
121+ export class CVCategoryGroup extends vscode . TreeItem {
122+ constructor (
123+ public readonly label : string ,
124+ public readonly collapsibleState : vscode . TreeItemCollapsibleState ,
125+ public readonly kind : 'mat' | 'plot' | 'pointcloud' ,
126+ public readonly children : ( CVGroup | CVVariable ) [ ]
127+ ) {
128+ super ( label , collapsibleState ) ;
129+ this . contextValue = `cvCategory:${ kind } ` ;
130+
131+ // Set icon based on category type
132+ if ( kind === 'mat' ) {
133+ this . iconPath = new vscode . ThemeIcon ( 'file-media' ) ;
134+ } else if ( kind === 'plot' ) {
135+ this . iconPath = new vscode . ThemeIcon ( 'graph' ) ;
136+ } else if ( kind === 'pointcloud' ) {
137+ this . iconPath = new vscode . ThemeIcon ( 'layers' ) ;
138+ }
139+ }
87140}
88141
89142export class CVGroup extends vscode . TreeItem {
@@ -94,20 +147,21 @@ export class CVGroup extends vscode.TreeItem {
94147 public readonly groupIndex ?: number
95148 ) {
96149 super ( label , collapsibleState ) ;
97- this . contextValue = 'cvGroup' ;
150+ // Only paired groups (with groupIndex) can have add button
151+ this . contextValue = groupIndex !== undefined ? 'cvGroup:paired' : 'cvGroup' ;
98152
99153 if ( groupIndex !== undefined ) {
100154 const color = COLORS [ groupIndex % COLORS . length ] ;
101- this . iconPath = getColoredIcon ( 'group' , color ) ;
155+ this . iconPath = getColoredCircleIcon ( color ) ;
102156 } else {
103- this . iconPath = new vscode . ThemeIcon ( 'symbol-group ' ) ;
157+ this . iconPath = new vscode . ThemeIcon ( 'circle-filled ' ) ;
104158 }
105159 }
106160}
107161
108- export class CVVariablesProvider implements vscode . TreeDataProvider < CVVariable | CVGroup > {
109- private _onDidChangeTreeData : vscode . EventEmitter < CVVariable | CVGroup | undefined | void > = new vscode . EventEmitter < CVVariable | CVGroup | undefined | void > ( ) ;
110- readonly onDidChangeTreeData : vscode . Event < CVVariable | CVGroup | undefined | void > = this . _onDidChangeTreeData . event ;
162+ export class CVVariablesProvider implements vscode . TreeDataProvider < CVVariable | CVGroup | CVCategoryGroup > {
163+ private _onDidChangeTreeData : vscode . EventEmitter < CVVariable | CVGroup | CVCategoryGroup | undefined | void > = new vscode . EventEmitter < CVVariable | CVGroup | CVCategoryGroup | undefined | void > ( ) ;
164+ readonly onDidChangeTreeData : vscode . Event < CVVariable | CVGroup | CVCategoryGroup | undefined | void > = this . _onDidChangeTreeData . event ;
111165
112166 private variables : CVVariable [ ] = [ ] ;
113167 private groups : CVGroup [ ] = [ ] ;
@@ -134,19 +188,38 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
134188 return this . variables ;
135189 }
136190
137- getTreeItem ( element : CVVariable | CVGroup ) : vscode . TreeItem {
191+ getTreeItem ( element : CVVariable | CVGroup | CVCategoryGroup ) : vscode . TreeItem {
138192 return element ;
139193 }
140194
141- async getChildren ( element ?: CVVariable | CVGroup ) : Promise < ( CVVariable | CVGroup ) [ ] > {
195+ async getChildren ( element ?: CVVariable | CVGroup | CVCategoryGroup ) : Promise < ( CVVariable | CVGroup | CVCategoryGroup ) [ ] > {
196+ // Level 3: Variable children (no children)
197+ if ( element instanceof CVVariable ) {
198+ return [ ] ;
199+ }
200+
201+ // Level 2: Group children (return variables in the group)
142202 if ( element instanceof CVGroup ) {
143203 return element . variables ;
144204 }
145205
146- if ( element instanceof CVVariable ) {
147- return [ ] ;
206+ // Level 1: Category children (return groups + unpaired variables)
207+ if ( element instanceof CVCategoryGroup ) {
208+ const result : ( CVGroup | CVVariable ) [ ] = [ ] ;
209+
210+ // Add paired groups
211+ const pairedGroups = element . children . filter ( c => c instanceof CVGroup && ( c as CVGroup ) . groupIndex !== undefined ) as CVGroup [ ] ;
212+ pairedGroups . sort ( ( a , b ) => ( a . groupIndex || 0 ) - ( b . groupIndex || 0 ) ) ;
213+ result . push ( ...pairedGroups ) ;
214+
215+ // Add unpaired variables
216+ const unpairedVars = element . children . filter ( c => c instanceof CVVariable && ! ( c as CVVariable ) . isPaired ) as CVVariable [ ] ;
217+ result . push ( ...unpairedVars ) ;
218+
219+ return result ;
148220 }
149-
221+
222+ // Level 0: Root (return category groups)
150223 const debugSession = vscode . debug . activeDebugSession ;
151224 if ( ! debugSession ) {
152225 this . variables = [ ] ;
@@ -561,7 +634,8 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
561634 pairedVars . length > 0 ? pairedVars . join ( ', ' ) : undefined ,
562635 groupIndex ,
563636 pointerInfo . isPointer ,
564- pointerInfo . baseType
637+ pointerInfo . baseType ,
638+ debugSession . id // Pass sessionId
565639 ) ;
566640 }
567641 return null ;
@@ -578,39 +652,73 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
578652
579653 this . variables = visualizableVariables ;
580654
581- const groupMap = new Map < number | undefined , CVVariable [ ] > ( ) ;
582- for ( const v of visualizableVariables ) {
583- const idx = v . groupIndex ;
584- if ( ! groupMap . has ( idx ) ) {
585- groupMap . set ( idx , [ ] ) ;
655+ // Separate variables by type
656+ const matVars = visualizableVariables . filter ( v => v . kind === 'mat' ) ;
657+ const plotVars = visualizableVariables . filter ( v => v . kind === 'plot' ) ;
658+ const pointcloudVars = visualizableVariables . filter ( v => v . kind === 'pointcloud' ) ;
659+
660+ const resultCategories : CVCategoryGroup [ ] = [ ] ;
661+
662+ // 1. Images (2D) category
663+ if ( matVars . length > 0 ) {
664+ const matChildren : ( CVGroup | CVVariable ) [ ] = [ ] ;
665+
666+ // Group paired mat variables
667+ const groupMap = new Map < number , CVVariable [ ] > ( ) ;
668+ for ( const v of matVars ) {
669+ if ( v . isPaired && v . groupIndex !== undefined ) {
670+ if ( ! groupMap . has ( v . groupIndex ) ) {
671+ groupMap . set ( v . groupIndex , [ ] ) ;
672+ }
673+ groupMap . get ( v . groupIndex ) ! . push ( v ) ;
674+ }
675+ }
676+
677+ // Create paired groups
678+ const sortedIndices = Array . from ( groupMap . keys ( ) ) . sort ( ( a , b ) => a - b ) ;
679+ for ( const idx of sortedIndices ) {
680+ const vars = groupMap . get ( idx ) ! ;
681+ matChildren . push ( new CVGroup (
682+ `Group ${ idx + 1 } ` ,
683+ vscode . TreeItemCollapsibleState . Expanded ,
684+ vars ,
685+ idx
686+ ) ) ;
586687 }
587- groupMap . get ( idx ) ! . push ( v ) ;
688+
689+ // Add unpaired mat variables
690+ const unpairedMatVars = matVars . filter ( v => ! v . isPaired ) ;
691+ matChildren . push ( ...unpairedMatVars ) ;
692+
693+ resultCategories . push ( new CVCategoryGroup (
694+ 'Images (2D)' ,
695+ vscode . TreeItemCollapsibleState . Expanded ,
696+ 'mat' ,
697+ matChildren
698+ ) ) ;
588699 }
589-
590- const resultGroups : CVGroup [ ] = [ ] ;
591- const sortedIndices = Array . from ( groupMap . keys ( ) )
592- . filter ( ( idx ) : idx is number => idx !== undefined )
593- . sort ( ( a , b ) => a - b ) ;
594700
595- for ( const idx of sortedIndices ) {
596- resultGroups . push ( new CVGroup (
597- `Group ${ idx + 1 } ` ,
701+ // 2. Plot (1D) category
702+ if ( plotVars . length > 0 ) {
703+ resultCategories . push ( new CVCategoryGroup (
704+ 'Plot (1D)' ,
598705 vscode . TreeItemCollapsibleState . Expanded ,
599- groupMap . get ( idx ) ! ,
600- idx
706+ 'plot' ,
707+ plotVars
601708 ) ) ;
602709 }
603-
604- const ungrouped = groupMap . get ( undefined ) ;
605- if ( ungrouped && ungrouped . length > 0 ) {
606- resultGroups . push ( new CVGroup (
607- '(ungrouped )' ,
710+
711+ // 3. Points (3D) category
712+ if ( pointcloudVars . length > 0 ) {
713+ resultCategories . push ( new CVCategoryGroup (
714+ 'Points (3D )' ,
608715 vscode . TreeItemCollapsibleState . Expanded ,
609- ungrouped
716+ 'pointcloud' ,
717+ pointcloudVars
610718 ) ) ;
611719 }
612720
613- return resultGroups ;
721+ return resultCategories ;
614722 } catch ( error ) {
615723 console . error ( 'Error fetching variables:' , error ) ;
616724 this . variables = [ ] ;
0 commit comments