1- use crate :: { model:: workbook:: Workbook , screen:: ScreenCommand , theme:: Theme } ;
1+ use crate :: {
2+ model:: { cell:: CellAddress , workbook:: Workbook } ,
3+ screen:: ScreenCommand ,
4+ theme:: Theme ,
5+ } ;
26
37use super :: { mode:: Selection , viewport:: Viewport } ;
48
@@ -12,6 +16,7 @@ pub struct TableContext {
1216 path : String ,
1317 wb : Workbook ,
1418 selection : Option < Selection > ,
19+ copied_region : Option < Selection > ,
1520 /// 模式需要切换屏幕时写入,随后由 `ModeHost` 取走。
1621 pending_command : Option < ScreenCommand > ,
1722}
@@ -25,6 +30,7 @@ impl TableContext {
2530 wb,
2631 viewport : Viewport :: new ( ) ,
2732 selection : None ,
33+ copied_region : None ,
2834 pending_command : None ,
2935 }
3036 }
@@ -64,6 +70,13 @@ impl TableContext {
6470 self . selection = None ;
6571 }
6672
73+ /// 已复制的区域(用于渲染虚线边框)。
74+ ///
75+ /// `None` 表示没有待粘贴的复制内容。
76+ pub fn copied_region ( & self ) -> Option < & Selection > {
77+ self . copied_region . as_ref ( )
78+ }
79+
6780 /// 光标所在单元格的原始文本。
6881 pub fn current_cell_raw ( & self ) -> String {
6982 self . wb
@@ -99,6 +112,89 @@ impl TableContext {
99112 self . wb . clear_cell ( self . viewport . cursor ( ) ) ;
100113 }
101114
115+ /// 复制当前选区的单元格内容到系统剪贴板。
116+ ///
117+ /// - 无选区 → 复制光标所在单元格
118+ /// - Range → 复制矩形区域内所有单元格
119+ /// - Row → 复制整行(所有列)
120+ /// - Column → 复制整列(所有行)
121+ ///
122+ /// 不改变选区或光标状态。
123+ pub fn copy_selection ( & mut self ) -> Result < ( ) , String > {
124+ let cells = self . collect_selection ( ) ;
125+ let tsv = crate :: clipboard:: to_tsv ( & cells) ;
126+ crate :: clipboard:: copy_to_clipboard ( & tsv) ?;
127+ self . copied_region = match & self . selection {
128+ Some ( sel) => Some ( * sel) ,
129+ None => {
130+ let cur = self . viewport . cursor ( ) ;
131+ Some ( Selection :: Range {
132+ anchor : cur,
133+ cursor : cur,
134+ } )
135+ }
136+ } ;
137+ Ok ( ( ) )
138+ }
139+
140+ /// 从系统剪贴板粘贴到光标位置。
141+ ///
142+ /// 自动扩展表格行列(不超过最大限制)。
143+ /// 粘贴后创建 Range 选区覆盖整个粘贴区域。
144+ /// 光标位置不变。
145+ pub fn paste_from_clipboard ( & mut self ) -> Result < ( ) , String > {
146+ let text = crate :: clipboard:: read_from_clipboard ( ) ?;
147+ let rows = crate :: clipboard:: from_tsv ( & text) ;
148+ if rows. is_empty ( ) {
149+ return Ok ( ( ) ) ;
150+ }
151+
152+ let paste_cols = rows[ 0 ] . len ( ) ;
153+ let paste_rows = rows. len ( ) ;
154+ let start = self . viewport . cursor ( ) ;
155+
156+ self . wb
157+ . ensure_size ( start. row + paste_rows, start. col + paste_cols) ;
158+
159+ for ( r, row_cells) in rows. iter ( ) . enumerate ( ) {
160+ let target_row = start. row + r;
161+ if target_row >= self . wb . rows {
162+ break ;
163+ }
164+ for ( c, cell_text) in row_cells. iter ( ) . enumerate ( ) {
165+ let target_col = start. col + c;
166+ if target_col >= self . wb . columns {
167+ break ;
168+ }
169+ let addr = CellAddress {
170+ row : target_row,
171+ col : target_col,
172+ } ;
173+ if cell_text. is_empty ( ) {
174+ self . wb . clear_cell ( addr) ;
175+ } else {
176+ self . wb . set_text ( addr, cell_text. clone ( ) ) ;
177+ }
178+ }
179+ }
180+
181+ let end_row = ( start. row + paste_rows) . min ( self . wb . rows ) . saturating_sub ( 1 ) ;
182+ let end_col = ( start. col + paste_cols)
183+ . min ( self . wb . columns )
184+ . saturating_sub ( 1 ) ;
185+
186+ self . copied_region = None ;
187+ self . set_selection ( Selection :: Range {
188+ anchor : start,
189+ cursor : CellAddress {
190+ row : end_row,
191+ col : end_col,
192+ } ,
193+ } ) ;
194+
195+ Ok ( ( ) )
196+ }
197+
102198 /// 清空当前选区,并退出选区状态。
103199 pub fn clear_selection_cells ( & mut self ) {
104200 if let Some ( spec) = self . selection_clear_spec ( ) {
@@ -135,4 +231,42 @@ impl TableContext {
135231 } ,
136232 } )
137233 }
234+
235+ fn collect_selection ( & self ) -> Vec < Vec < String > > {
236+ match & self . selection {
237+ None => {
238+ vec ! [ vec![ self . current_cell_raw( ) ] ]
239+ }
240+ Some ( Selection :: Range { anchor, cursor } ) => {
241+ let r1 = anchor. row . min ( cursor. row ) ;
242+ let r2 = anchor. row . max ( cursor. row ) ;
243+ let c1 = anchor. col . min ( cursor. col ) ;
244+ let c2 = anchor. col . max ( cursor. col ) ;
245+ ( r1..=r2)
246+ . map ( |r| ( c1..=c2) . map ( |c| self . cell_raw_at ( r, c) ) . collect ( ) )
247+ . collect ( )
248+ }
249+ Some ( Selection :: Row ( r) ) => {
250+ let r = * r;
251+ vec ! [
252+ ( 0 ..self . wb. columns)
253+ . map( |c| self . cell_raw_at( r, c) )
254+ . collect( ) ,
255+ ]
256+ }
257+ Some ( Selection :: Column ( c) ) => {
258+ let c = * c;
259+ ( 0 ..self . wb . rows )
260+ . map ( |r| vec ! [ self . cell_raw_at( r, c) ] )
261+ . collect ( )
262+ }
263+ }
264+ }
265+
266+ fn cell_raw_at ( & self , row : usize , col : usize ) -> String {
267+ self . wb
268+ . get_cell ( CellAddress { row, col } )
269+ . map ( |cell| cell. raw . clone ( ) )
270+ . unwrap_or_default ( )
271+ }
138272}
0 commit comments