api-issue-tracker icon indicating copy to clipboard operation
api-issue-tracker copied to clipboard

`UI::scale_factor` does not return correct value - always returns scaling for primary display

Open DanRathbun opened this issue 2 years ago • 6 comments

SketchUp Ruby API Issue

UI::scale_factor does not return correct value - always returns scaling for primary display

VERSION: 2022

This method does not return correct value when SketchUp runs on external display. It always returns the display scaling of the primary display. So for example, if the primary display is scaled to 125% (as recommended) but SketchUp is running on an external secondary display scaled at 100%, this method returns 1.25.

The issue is seen on Windows, but there are also other issues on the Mac platform.


Should this method have an optional argument to get the scaling factor for a specific display in the system's display set ? (Ie, where no argument is 0, being the primary display.)

Should the Sketchup or UI module have a getter method to return the display number that SketchUp is running on ?

scale = UI::scale_factor(display: Sketchup.display_number)

And then, if the UI::HtmlDialog class also got a #display_number instance method, we could do:

scale = UI::scale_factor(display: @dialog.display_number)

The number should be set when it is shown. But have a default of 0 after instancing before shown.


RELATED ISSUES:

DanRathbun avatar Jan 23 '23 17:01 DanRathbun

I encountered this issue when using an external monitor on my Mac, and this is the temporary workaround I am using to resolve it:

def self.verify_ui_scale
  dialog = UI::HtmlDialog.new(dialog_title: "ui_scale", width: 300, height: 300)

  html = <<-EOT
    <!DOCTYPE html>
    <html><script> window.onload = function() { sketchup.ready(window.outerWidth, window.devicePixelRatio) }; </script></html>
  EOT

  dialog.add_action_callback("ready") do |_a, _width, pixel_ratio|
    self.scale_factor = pixel_ratio
    dialog.close
    Sketchup.focus if Sketchup.respond_to?(:focus)
  end

  dialog.set_html(html)
  dialog.center
  dialog.show
end

I wonder if there is a better solution?

voqhai avatar Mar 02 '23 04:03 voqhai

Does the dialog.center call ensure that the dialog window is opened upon the correct display monitor ?

DanRathbun avatar Mar 02 '23 18:03 DanRathbun

Logged as: SKEXT-3654

sketchup[bot] avatar Mar 13 '23 06:03 sketchup[bot]

I encountered this issue when using an external monitor on my Mac, and this is the temporary workaround I am using to resolve it: ...

This "workaround" does not work on Windows platform. The pixel ratio returned on a 100% scaled display still returns 1.25.

DanRathbun avatar Mar 13 '23 19:03 DanRathbun

I just tested the Sketchup::View#text_bounds method which states that it uses UI.scale_factor to scale the text drawn by View#draw_text.

Weirdly, the text is scaled correctly on the external 150% display even though UI.scale_factor returns 1.25 for the internal "Main" 125% display.

So, my conclusion is that it should then be possible to fix the UI.scale_factor method if the Sketchup::View#text_bounds method can get it right.


@voqhai I wonder if there is a better solution?

As to a workaround, .. perhaps the following ...

It might also show a bug ... that it always scales to the external display regardless of which display SketchUp is on.

  def display_scaling
    position = Geom::Point3d.new(200, 200, 0)
    view = Sketchup.active_model.active_view
    want = 20.0
    high = view.text_bounds(position, 'Hello SketchUp', font: 'Arial', size: want).height
    high / want
  end

DanRathbun avatar Jul 12 '23 15:07 DanRathbun

I have experimented with Sketchup::View#text_bounds, but it seems to be ineffective on macOS. I have found an alternative approach that works pretty effectively (not tested on Windows yet).

def self.true_ui_scale(view = Sketchup.active_model.active_view)
  cam = view.camera
  center = cam.target
  origin_size = 100
  d = view.pixels_to_model(origin_size, center)

  point = center.offset(cam.up, d)
  line3d = [center, point]
  line2d = line3d.map { |pt| view.screen_coords(pt) }
  screen_size = line2d[0].distance(line2d[1])

  (screen_size / origin_size).round(1)
end

voqhai avatar Aug 14 '23 03:08 voqhai