@@ -111,13 +111,8 @@ impl EditorApp {
111111 // Save scroll area rect for auto-scroll during selection
112112 let scroll_area_rect = ui. clip_rect ( ) ;
113113
114- // Create clickable area over the FULL height
115- let full_area = ui. available_rect_before_wrap ( ) ;
116- let sense_rect = ui. interact (
117- full_area,
118- ui. id ( ) . with ( "editor_click_area" ) ,
119- egui:: Sense :: click ( ) ,
120- ) ;
114+ // Remove manual clickable area over FULL height.
115+ // We will use min_size on the TextEdit instead.
121116
122117 ui. horizontal_top ( |ui| {
123118 if show_line_numbers {
@@ -154,7 +149,7 @@ impl EditorApp {
154149
155150 // Iterate lines, including trailing empty line after final \n
156151 let mut lines_iter = text_str. split ( '\n' ) . peekable ( ) ;
157- let mut line_idx = 0 ;
152+ let mut _line_idx = 0 ;
158153 while let Some ( line) = lines_iter. next ( ) {
159154 let is_last = lines_iter. peek ( ) . is_none ( ) ;
160155
@@ -260,17 +255,13 @@ impl EditorApp {
260255 byte_offset += line. len ( ) ;
261256 }
262257
263- line_idx += 1 ;
258+ _line_idx += 1 ;
264259 }
265260
266261 ui. fonts ( |f| f. layout_job ( layout_job) )
267262 } ;
268263
269- // Add invisible blocking layer during middle mouse
270- if middle_button_active {
271- let overlay_rect = ui. max_rect ( ) ;
272- ui. allocate_rect ( overlay_rect, egui:: Sense :: click_and_drag ( ) ) ;
273- }
264+ // Panning blocking handled by .interactive(!middle_button_active) in TextEdit
274265
275266 if ui. memory ( |mem| mem. has_focus ( text_edit_id) ) {
276267 if ui. input_mut ( |i| i. consume_key ( egui:: Modifiers :: NONE , egui:: Key :: Tab ) ) {
@@ -300,18 +291,32 @@ impl EditorApp {
300291 }
301292 }
302293
303- let available_width = if word_wrap {
304- ui. available_width ( )
294+ // Visible viewport width for the text area (excluding line numbers)
295+ let viewport_width = if show_line_numbers {
296+ ( full_clip_rect. width ( ) - line_number_width - text_left_padding) . max ( 100.0 )
297+ } else {
298+ full_clip_rect. width ( ) . max ( 100.0 )
299+ } ;
300+
301+ // desired_width controls text wrapping:
302+ // word_wrap ON -> wrap at viewport width
303+ // word_wrap OFF -> no wrapping (INFINITY)
304+ let desired_width = if word_wrap {
305+ viewport_width
305306 } else {
306307 f32:: INFINITY
307308 } ;
309+
310+ let min_height = full_clip_rect. height ( ) ;
308311
309312 let output = egui:: TextEdit :: multiline ( text)
310313 . id ( text_edit_id)
311314 . font ( egui:: TextStyle :: Monospace )
312315 . code_editor ( )
313- . desired_width ( available_width )
316+ . desired_width ( desired_width )
314317 . desired_rows ( line_count)
318+ // min_size ensures TextEdit fills the visible area but is NEVER infinite
319+ . min_size ( egui:: vec2 ( viewport_width, min_height) )
315320 . frame ( false )
316321 . lock_focus ( true )
317322 . interactive ( !middle_button_active)
@@ -324,7 +329,7 @@ impl EditorApp {
324329 let galley = & output. galley ;
325330
326331 // AUTO-SCROLL during text selection with left button
327- if pointer . primary_down ( ) && !middle_button_active {
332+ if output . response . dragged ( ) && !middle_button_active {
328333 if let Some ( pointer_pos) = pointer. latest_pos ( ) {
329334 let scroll_speed = 15.0 ;
330335 let scroll_margin = 30.0 ;
@@ -343,42 +348,19 @@ impl EditorApp {
343348 scroll_state. offset . x += scroll_speed;
344349 }
345350
351+ let content_rect = galley. rect ;
352+ let max_offset_y = ( content_rect. height ( ) - scroll_area_rect. height ( ) ) . max ( 0.0 ) ;
353+ let max_offset_x = ( content_rect. width ( ) - scroll_area_rect. width ( ) ) . max ( 0.0 ) ;
354+ scroll_state. offset . y = scroll_state. offset . y . clamp ( 0.0 , max_offset_y) ;
355+ scroll_state. offset . x = scroll_state. offset . x . clamp ( 0.0 , max_offset_x) ;
356+
346357 scroll_state. store ( ui. ctx ( ) , scroll_id) ;
347358 }
348359 }
349360
350- // Calculate content height
351- let content_height = if !galley. rows . is_empty ( ) {
352- galley. rows . last ( ) . unwrap ( ) . max_y ( )
353- } else {
354- 0.0
355- } ;
356- let content_bottom = text_rect. min . y + content_height;
357-
358- // Detect click in empty space BELOW text (only when not middle mouse)
359- if sense_rect. clicked ( ) && !middle_button_active {
360- if let Some ( pointer_pos) = ui. input ( |i| i. pointer . interact_pos ( ) ) {
361- if pointer_pos. y > content_bottom && pointer_pos. x >= text_rect. min . x {
362- // Clicked below content - move cursor to end
363- let end_pos = text. chars ( ) . count ( ) ;
364- if let Some ( mut state) =
365- egui:: TextEdit :: load_state ( ui. ctx ( ) , text_edit_id)
366- {
367- state. cursor . set_char_range ( Some (
368- egui:: text:: CCursorRange :: one ( egui:: text:: CCursor :: new (
369- end_pos,
370- ) ) ,
371- ) ) ;
372- state. store ( ui. ctx ( ) , text_edit_id) ;
373- }
374- self . highlighted_line = Some ( line_count) ;
375- ui. memory_mut ( |mem| mem. request_focus ( text_edit_id) ) ;
376- } else if pointer_pos. y <= content_bottom {
377- // Clicked within text area - request focus
378- ui. memory_mut ( |mem| mem. request_focus ( text_edit_id) ) ;
379- }
380- }
381- }
361+
362+ // Removed custom empty-space handling below text,
363+ // as TextEdit now fills the visual area.
382364
383365 // Restore full clip rect for drawing line numbers and separator
384366 ui. set_clip_rect ( full_clip_rect) ;
0 commit comments