المتابعون للمدونة

الأربعاء، 7 يونيو 2017

TransparentRTFViewer

العارض الشفاف للنص المنسق
TransparentRTFViewer
 
هناك مشروع مفتوح المصدر مكتوب بسي شارب اسمه SubtitleEdit (مزيد من التفاصيل عنه وكيفية تحميله في هذا الموضوع)، وقد وجدت فيه بعض المشاكل في عرض نص الترجمة على نافذة الفيديو.. فمثلا: استخدم المبرمج مربع نص منسق RichTextBox لعرض نص الترجمة فوق الفيديو، ومنحه ارتفاعا ثابتا.. لكن إذا زاد عدد السطور المعروضة عن سطرين، أو حتى إذا تم تكبير الخط من نافذة الإعدادات، فسيكون ارتفاع النص أكبر من الارتفاع الذي حدده لمربع النص، ولن يظهر جزء من الترجمة.. وقد حللت هذه المشكلة كالتالي:

في الحدث TextChanged الخاص بمربع النص المنسق، يمكن قياس ارتفاع النص باستخدام الوسيلة Graphics.MeasureString.. لقد أضفت هذا الكود داخل الفئة VideoPlayerContainer في المشروع:
if (_subtitleTextBox.Text.Length == 0) {
   _subtitleTextBox.Show();
   return;
}
        
using (Graphics g = CreateGraphics()){
   _subtitlesHeight = (int)g.MeasureString(
     _subtitleTextBox.Text, _subtitleTextBox.Font,  
     _subtitleTextBox.Width).Height;
}
وبهذا كلما تغير المشهد المعروض في الفيديو، يتم حساب ارتفاع نص الترجمة وتغيير ارتفاع مربع النص ليستوعبه.
لكن ظلت هناك مشكلة تزعجني، وهي عدم وجود طريقة لجعل خلفية مربع النص المنسق شفافة لتظهر الترجمة على الفيديو بدون إخفاء جزء منه وهذا قد يخفي نصف شاشة الفيديو تقريبا بسبب الترجمة.
جربت كذلك عرض الترجمة في لافتة، ومن الممكن جعل خلفيتها شفافة، لكنها تشف عن لون خلفية النافذة، وليس خلفية الفيديو!
هذا الموضوع مهم لي لأني أريد إضافة إمكانية تحرير الفيديو إلى هذا المشروع، بحذف بعض اللقطات منه، وما يرتبط بهذا من تعديل لنص الترجمة المناظر بالحذف وتعديل التوقيت.. يجب أن تكون مشاهدة الفيديو مريحة حتى يكون البرنامج أداة مشاهدة وتحرير جيدة.
وبالبحث على الإنترنت واستشارة الأصدقاء (شكرا للمبرمج المبدع أ. خالد السعداني) توصلنا إلى طريقة لجعل خلفية مربع النص المنسق شفافة، وذلك بوراثته وإنشاء نسخة معدلة منه، يتم فيها إعادة كتابة الخاصية المحمية CreateParams لمنع رسم خلفية مربع النص، وبهذا يظهر الكلام المكتوب فيه فقط كأنه مكتوب على ما تحت مربع النص.
هذا هو كود هذه الخاصية:
protected override CreateParams CreateParams {
    get {
     //This makes the control's background transparent
     CreateParams CP = base.CreateParams;
     CP.ExStyle |= 0x20;
     return CP;
      }
 }
لكن هناك مشكلة في هذه الطريقة، وهي أنها لا تستدعي الحدث Paint (لمنع رسم خلفية مربع النص)، وبالتالي لا يمكن إنعاش مربع النص.. فمثلا لو حددت جزءا من النص فسيظل محددا حتى لو حددت جزءا غيره، ولو كتبت حروفا جديدة في النص فستتداخل مع الحروف السابقة.. الأداة فعلا شفافة، لكنها بهذا الشكل غير عملية.
وقد توصلت بعد محاولات إلى أن إخفاء مربع النص وإعادة إظهاره يقوم بإنعاش رسمه (بدلا من للحدث Paint) وهذا يجعل النص يظهر بشكل صحيح.. لهذا يجب استخدام الحدث TextChanged لإخفاء مربع النص ثم إعادة إظهاره كلما تغير النص المعروض فيه.
لكن تتبقى مشكلة تحديد النص.
يمتلك مربع النص المنسق حدثا اسمه SelectionChanged  ويمكن استخدامه لإخفاء وعرض مربع النص بنفس الطريقة.. لكن هذا غير ضروري هنا، لأننا نعرض نص ترجمة على فديو، وليس مسموحا للمستخدم بتحديد هذا النص أو تغييره أصلا.. لهذا السبب قام المبرمج بجعل مربع النص للعرض فقط وغير قابل للتحديد، وذلك بتعديل بعض خصائص الفئة الموروثة من مربع النص المنسق في حدث إنشائها Constructor، كالتالي:
جعل مربع النص للقراءة فقط:
ReadOnly = true;
وأزال الإطار من حوله:
BorderStyle = BorderStyle.None;
ومنع انتقال المستخدم إليه عند ضغط الزر Tab من لوحة المفاتيح:
TabStop = false;
وجعله غير قابل للتحديد، أي لا يمكنه استقبال مؤشر الكتابة:
SetStyle(ControlStyles.Selectable, false);
وأوقف استجابة نظام الويندوز لأحداث الفأرة الخاصة بمربع النص:
SetStyle(ControlStyles.UserMouse, true);
وأضاف معالجا لحدث دخول الفأرة فوق مربع النص، وكتب فيه أمرا يجعل شكل مؤشر الفأرة عاديا (ليمنعها من أن تتحول إلى شعاع الكتابة فوق مربع النص):
MouseEnter += delegate { Cursor = Cursors.Default; };
ومنع ظهور المنزلقين الأفقي والراسي:
ScrollBars = RichTextBoxScrollBars.None;
وبهذا يكون قد أخفى كل معالم مربع النص عن عين المستخدم، ولم يتبق إلا قدجرة المستخدم على تحديد مربع النص بزر الفأرة.. لمنع هذا استخدم الوسيلة المحمية WndProc التي تستقبل كل رسائل الويندوز (أي الأحداث التي يطلقها نظام الويندوز وهو ينجز مهام النافذة) حيث قام بإيقاف تمرير الرسالتين الخاصتين بضغط زر الفأرة وترك زر الفأرة، وهذا سيمنع المستخدم من ضغط مربع النص بالفأرة، وسيمنعه أيضا من تحديد النص بالفأرة.
هذا هو كود هذه الفئة كاملة، وقد أسميتها العارض الشفاف للنص المنسق TransparentRTFViewer:
public class TransparentRTFViewer : RichTextBox {
    public TransparentRTFViewer() {
        ReadOnly = true;
        BorderStyle = BorderStyle.None;
        TabStop = false;
        SetStyle(ControlStyles.Selectable, false);
        SetStyle(ControlStyles.UserMouse, true);
        MouseEnter += delegate {
           Cursor = Cursors.Default;
        };
        ScrollBars = RichTextBoxScrollBars.None;
        Margin = new Padding(0);
    }
 
 
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x204) // WM_RBUTTONDOWN
            return;  // حجب الرسالة
        if (m.Msg == 0x205) // WM_RBUTTONUP
            return;  // حجب الرسالة
        base.WndProc(ref m); // تمرير باقي الرسائل
    }
 
    protected override CreateParams CreateParams
    {
        get
        {
        //This makes the control's background transparent
        CreateParams CP = base.CreateParams;
        CP.ExStyle |= 0x20;
        return CP;
        }
    }
}
يمكنكم الآن استخدام هذه الفئة لعرض أي نصوص منشقة بالخطوط والألوان بخلفية شفافة.
 

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحة الشاعر