Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1474

VB6 QR-Encoding+Decoding and IME-Window-Positioning

$
0
0
This Demo depends on vbRichClient5 (version 5.0.21 and higher), as well as the latest vbWidgets.dll.
One can download both new packages from the Download-page at: http://vbrichclient.com/#/en/Downloads.htm
(vbWidgets.dll needs to be extracted from the GitHub-Download-Zip and placed beside vbRichClient5.dll,
there's small "RegisterInPlace-Scripts" for both Dll-Binaries now).

After both dependencies were installed, one can load the below Demo-Project into the VB-IDE:
QRandIMEDemo.zip

According to the Title of this Thread, we try to show the:

Free positioning of an IME-Window:
delegating its IME_Char-Messages into free choosable Widget-controls (the Demo does
that against cwTextBox-Widgets exclusively - but could accomplish that also against cwLabels
or cwImages.

Here is the interesting part (the IME-API-Declarations and Wrapper-Functions are left out),
which is contained in the new cIME-Class (available in the Code-Download from the vbWidgets-GitHub-Repo):

Code:

'RC5-SubClasser-Handler
Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
Const WM_IME_SETCONTEXT = 641, WM_IME_STARTCOMPOSITION = 269, WM_IME_CHAR = 646
On Error GoTo 1

  Select Case Msg
      Case WM_IME_SETCONTEXT
        SwitchOpenStatus wParam
       
      Case WM_IME_STARTCOMPOSITION
        HandleIMEPos
     
      Case WM_IME_CHAR
        Dim WFoc As cWidgetBase, KeyCode As Integer
        Set WFoc = FocusedWidget: KeyCode = CInt("&H" & Hex(wParam And &HFFFF&))
        If Not WFoc Is Nothing Then
          If WFoc.Key = tmrFoc.Tag Then RaiseEvent HandleIMEChar(WFoc, KeyCode, ChrW(KeyCode))
        End If
        Exit Sub 'handled ourselves - so we skip the default message-handler at the end of this function
  End Select
 
1: Result = SC.CallWindowProc(Msg, wParam, lParam)
End Sub
 
Private Sub tmrFoc_Timer()
  HandleIMEPos
End Sub

Private Function FocusedWidget() As cWidgetBase
  If Cairo.WidgetForms.Exists(hWnd) Then Set FocusedWidget = Cairo.WidgetForms(hWnd).WidgetRoot.ActiveWidget
End Function

Private Sub HandleIMEPos()
Dim WFoc As cWidgetBase, AllowIME As Boolean
On Error GoTo 1

  Set WFoc = FocusedWidget
  If WFoc Is Nothing Then
    tmrFoc.Tag = ""
  Else
    RaiseEvent HandleIMEPositioning(WFoc, AllowIME)
    If AllowIME Then tmrFoc.Tag = WFoc.Key
  End If
 
1: SwitchOpenStatus AllowIME
End Sub

As one can see, this Class is (currently) only raising two Events to the outside -
received by a hosting (RC5) cWidgetForm-Class.

The elsewhere mentioned problems with "forcibly ANSIed" IME-WChars do not happen in
this Demo, because of a "full queue of W-capable APIs" (including a W-capable MessageLoop,
which is available in the RC5 per Cairo.WidgetForms.EnterMessageLoop...

The Integration of an RC5-cWidgetForm into an existing VB6-Project is relative easy (no need
to rewrite everything you have) - this Demo shows how one can accomplish that, by showing
the RC5-Form modally - starting from a normal VB-Form-CommandButton:

Here's all the code in the normal VB6-Starter-Form, which accomplishes that:
Code:

Option Explicit

Private VBFormAlreadyUnloaded As Boolean

Private Sub cmdShowRC5IMEForm_Click()
  With New cfQRandIME ' instantiate the RC5-FormHosting-Class
 
    .Form.Show , Me 'this will create and show the RC5-Form with the VB-Form as the underlying Parent
   
    'now we enter the W-capable RC5-message-pump, which will loop "in  place" till the RC5-Form gets closed again
    Cairo.WidgetForms.EnterMessageLoop True, False
 
    'the RC5-Form was closed, so let's read-out the Public Vars of its hosting cf-Class
    If Not VBFormAlreadyUnloaded Then '<- ... read the comment in Form_Unload, on why we need to check this flag
      Set Picture1.Picture = .QR1.QRSrf.Picture
      Set Picture2.Picture = .QR2.QRSrf.Picture
    End If
  End With
End Sub

Private Sub Form_Unload(Cancel As Integer) 'this can happen whilst the RC5-ChildForm is showing, ...
  VBFormAlreadyUnloaded = True  'so we set a Flag, to not implicitely load this VB-ParentForm again, when filling the Result-PicBoxes
End Sub

Private Sub Form_Terminate() 'the usual RC5-cleanup call (when the last VB-Form was going out of scope)
  If Forms.Count = 0 Then New_c.CleanupRichClientDll
End Sub

The above Starter-Form (fMain.frm) will look this way


And pressing the CommandButton, it will produce the modal RC5-WidgetForm:


What one can see above is two (cwTextBox-based) Edit-Widgets - and the left one
is showing the free positioned IME-Window - the IME-Window (when visible), will
jump automatically, as soon as the user switches the Input-Focus to a different Widget.

To test this in a bit more extreme scenario even, I've made the two cwQRSimple-Widgets
(in the lower section of the Form) movable - and in case the IME-Window is shown
below one of them as in this ScreenShot:


... the IME-Window will follow the currently focused QR-Widget around, when it's dragged
with the Mouse...

Here's the complete code of the cfQRandIME.cls (which hosts the RC5-cWidgetForm-instance):
Code:

Option Explicit

Public WithEvents Form As cWidgetForm, WithEvents IME As cIME

Public QREnc As New cQREncode, QRDec As New cQRDecode 'the two (non-visible) QR-CodecClass-Vars
Public TB1 As cwTBoxWrap, TB2 As cwTBoxWrap 'the two TextBox-Wrapper-Classes
Public QR1 As cwQRSimple, QR2 As cwQRSimple 'the two QR-Widgets
 
Private Sub Class_Initialize()
  Set Form = Cairo.WidgetForms.Create(vbFixedDialog, "QR-Widgets and IME-Window-Positioning", , 800, 600)
      Form.IconImageKey = "QRico2"
      Form.WidgetRoot.ImageKey = "bgPatForm"
      Form.WidgetRoot.ImageKeyRenderBehaviour = ImgKeyRenderRepeat
     
  Set IME = New cIME 'create the vbWidgets.cIME-instance
      IME.BindToForm Form '...and bind our cWidgetForm-instance to it (IME will throw two Events at us then)
End Sub

Private Sub Form_Load() 'handle Widget-Creation and -Adding on this Form
  Form.Widgets.Add(New cwSeparatorLabel, "Sep1", 11, 8, Form.ScaleWidth - 22, 42).SetCaptionAndImageKey "EditBox-DemoArea", "Edit", &H11AA66
    Set TB1 = Form.Widgets.Add(New cwTBoxWrap, "TB1", 25, 60, 280, 38)
        TB1.TBox.CueBannerText = "Session-Login..."
        TB1.Widget.ImageKey = "session1"
    Set TB2 = Form.Widgets.Add(New cwTBoxWrap, "TB2", 325, 60, 280, 38)
        TB2.TBox.CueBannerText = "Place some Info here..."
        TB2.Widget.ImageKey = "info1"
     
  Form.Widgets.Add(New cwSeparatorLabel, "Sep2", 11, 155, Form.ScaleWidth - 22, 42).SetCaptionAndImageKey "QRCode-DemoArea", "Preview", &H1030EE
    Set QR1 = Form.Widgets.Add(New cwQRSimple, "QR1", 25, 240, 250, 220)
    Set QR2 = Form.Widgets.Add(New cwQRSimple, "QR2", 325, 280, 250, 220)
End Sub

Private Sub Form_BubblingEvent(Sender As Object, EventName As String, P1 As Variant, P2 As Variant, P3 As Variant, P4 As Variant, P5 As Variant, P6 As Variant, P7 As Variant)
  If EventName = "Change" And TypeOf Sender Is cwTextBox Then 'we handle the Change-Event of the QRWidget-Child-Textboxes here
    If Not (Sender Is QR1.TBox Or Sender Is QR2.TBox) Then Exit Sub
   
    'resolve to the (TextBox-Hosting) cwQRSimple-Widget in question
    Dim QR As cwQRSimple: Set QR = IIf(Sender Is QR1.TBox, QR1, QR2)
   
    'Encode the current Text of our QR-Widget - and place the returned Pixel-Surface in QR.QRSrf
    Set QR.QRSrf = QREnc.QREncode(New_c.Crypt.VBStringToUTF8(QR.Text))
 
    'to verify, we perform a true Decoding of the QR-Text from the Pixels of the just created QR-Widgets QR-Surface
    QRDec.DecodeFromSurface QR.QRSrf
    'and reflect this decoded Unicode-StringResult in the Caption of the QR-Widget (so, ideally QR.Caption should match QR.Text)
    If QRDec.QRDataLen(0) Then QR.Caption = New_c.Crypt.UTF8ToVBString(QRDec.QRData(0)) Else QR.Caption = ""
  End If
 
  'the QR-Widgets (cwQRSimple) are moveable - and in case they have an active IME-Window, we will move that too
  If EventName = "W_Moving" And TypeOf Sender Is cwQRSimple Then IME_HandleIMEPositioning Sender.TBox.Widget, True
End Sub

Private Sub IME_HandleIMEPositioning(FocusedWidget As cWidgetBase, AllowIME As Boolean)
  If TypeOf FocusedWidget.Object Is cwTextBox Then
    AllowIME = True '<- here we allow IME-Windows only for cwTextBox-Widgets (but we could also allow IME on other Widget-Types)
    IME.SetPosition FocusedWidget.AbsLeftPxl + 3, FocusedWidget.AbsTopPxl + FocusedWidget.ScaleHeightPxl + 4
  End If
End Sub

Private Sub IME_HandleIMEChar(FocusedWidget As cWidgetBase, ByVal IMEKeyCode As Integer, IMEWChar As String)
  FocusedWidget.KeyPress IMEKeyCode 'simply delegate the incoming IMEKeyCode into the Widget in question
  'the above is the more generic delegation-method into any Widget (which are all derived from cWidgetBase)
 
  '*alternatively* (for cwTextBoxes, which is the only Widget-Type we allow IME for in this Demo here)
  'we could also use:
'  Dim TB As cwTextBox
'  Set TB = FocusedWidget.Object
'      TB.SelText = IMEWChar
End Sub

Note the two blue marked EventHandlers at the bottom of the above code-section, which
make use of the two cIME-Events, which were mentioned at the top of this posting.


QR-Code Generation and Decoding:


The base QR-Encoding/Decoding-support is now included in vb_cairo_sqlite.dll (from two C-libs which are now statically contained).
And the vbWidgets.dll project contains the two Wrapper-Classes (cQREncode, cQRDecode) for these new exposed APIs.

cQREncode/cQRDecode is used in conjunction with thrown Change-Events of our cwQRSimple-Widgets
(which you saw in the ScreenShot above).

Here's the central Eventhandler which is contained in the RC5-WidgetForm-Hosting Class (cfQrandIME):
Code:

Private Sub Form_BubblingEvent(Sender As Object, EventName As String, P1, P2, P3, P4, P5, P6, P7)
  If EventName = "Change" And TypeOf Sender Is cwTextBox Then 'we handle the Change-Event of the QRWidget-Child-Textboxes here
    If Not (Sender Is QR1.TBox Or Sender Is QR2.TBox) Then Exit Sub
   
    'resolve to the (TextBox-Hosting) cwQRSimple-Widget in question
    Dim QR As cwQRSimple: Set QR = IIf(Sender Is QR1.TBox, QR1, QR2)
   
  'Encode the current Text of our QR-Widget - and place the returned Pixel-Surface in QR.QRSrf
    Set QR.QRSrf = QREnc.QREncode(New_c.Crypt.VBStringToUTF8(QR.Text))
 
    'to verify, we perform a true Decoding of the QR-Text from the Pixels of the just created QR-Widgets QR-Surface
    QRDec.DecodeFromSurface QR.QRSrf
    'and reflect this decoded Unicode-StringResult in the Caption of the QR-Widget (so, ideally QR.Caption should match QR.Text)
    If QRDec.QRDataLen(0) Then QR.Caption = New_c.Crypt.UTF8ToVBString(QRDec.QRData(0)) Else QR.Caption = ""
  End If
 
  'the QR-Widgets (cwQRSimple) are moveable - and in case they have an active IME-Window, we will move that too
  If EventName = "W_Moving" And TypeOf Sender Is cwQRSimple Then IME_HandleIMEPositioning Sender.TBox.Widget, True
End Sub

So that's quite simple as far as QR-codes are concerned (because of the Bubbling-Event-mechanism of the
RC5-WidgetEngine - but also due to the quite powerful Cairo-ImageSurface-Objects, which are used in the
cQREncode/Decode-classes to transport the encoded (or to be decoded) Pixel-Information.

From a cCairoSurface it is possible, to write to PNG-, or JPG-ByteArrays or -Files at any time,
so exporting of the QR-Code-Images is not covered by this Demo - but would require only
a line of Code or two, in concrete adaptions of the above example.

Have fun,

Olaf
Attached Files

Viewing all articles
Browse latest Browse all 1474

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>